diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 195d65e12ac7ede7a092753696635f88c88afad4..4ea1cf266cce4c73951bcea4cee9f0649b87fc7c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -11,3 +11,14 @@ sast:
   stage: test
 include:
 - template: Security/SAST.gitlab-ci.yml
+
+
+ansible-lint:
+  stage: test
+  image:
+    name: nixos/nix
+    entrypoint: [""]
+  script:
+    - cd tools/nix
+    - nix --extra-experimental-features nix-command --extra-experimental-features flakes develop .#ansible --command bash -c "cd ../../40_ansible/; ansible-lint"
+    - nix --extra-experimental-features nix-command --extra-experimental-features flakes develop .#ansible --command bash -c "cd ../../40_ansible/filter_plugins/; ./run_tests.sh"
diff --git a/40_ansible/filter_plugins/dimail_filters/cert_info_filters_test.py b/40_ansible/filter_plugins/dimail_filters/cert_info_filters_test.py
index fb4f9356ff41be10518c4698ca3887091c22f4ab..728ec73dd156fd3a4f7b4f579964e99d4eca93c2 100644
--- a/40_ansible/filter_plugins/dimail_filters/cert_info_filters_test.py
+++ b/40_ansible/filter_plugins/dimail_filters/cert_info_filters_test.py
@@ -93,17 +93,18 @@ def hostvars():
 def cert_user_group(): 
     return ["main-example", "backup-example"]
 
+@pytest.fixture
+def groups():
+    return ['imap_master', 'smtp']
 
-def test_cert_info_tech_domain(checked_tech_domain):
-    cert_info_tech = cert_info.cert_info_tech_domain(checked_tech_domain)
+def test_cert_info_tech_domain(checked_tech_domain, groups):
+    cert_info_tech = cert_info.cert_info_tech_domain(checked_tech_domain, groups)
     assert cert_info_tech == {
         'cert_name': 'cert_name',
         'domain': 'tech.example.com',
-        'domains': ['api.tech.example.com',
-                    'imap.tech.example.com',
+        'domains': ['imap.tech.example.com',
                     'smtp.tech.example.com',
-                    'mx.tech.example.com',
-                    'auth.tech.example.com']
+                    'mx.tech.example.com']
     }
 
 def test_cert_info_host(cert_user_group, hostvars, host_domain):
@@ -124,13 +125,13 @@ def test_cert_info_domain(mail_domain):
             'webmail.mail.test.example.com',
     ]}
 
-def test_raw_tech_domain_is_detected(cert_user_group, raw_tech_domain):
+def test_raw_tech_domain_is_detected(cert_user_group, raw_tech_domain, groups):
     # given
     hostname = cert_user_group[0]
 
     # when
     with pytest.raises(AnsibleFilterError) as context:
-        cert_info.cert_info_tech_domain(raw_tech_domain)
+        cert_info.cert_info_tech_domain(raw_tech_domain, groups)
 
     # then
     assert "error: tech_domain has not been filtered with dimail_check_tech_domain" in str(context.value)
diff --git a/40_ansible/filter_plugins/dimail_filters/compute_mail_domain_filters_test.py b/40_ansible/filter_plugins/dimail_filters/compute_mail_domain_filters_test.py
index 942cfb7d94ad00b656ec12b6edb6aa4cf2bccb20..eff74025f4159c6cebe3da02027b7675d6c11bb3 100644
--- a/40_ansible/filter_plugins/dimail_filters/compute_mail_domain_filters_test.py
+++ b/40_ansible/filter_plugins/dimail_filters/compute_mail_domain_filters_test.py
@@ -14,6 +14,8 @@ def mailbox_client_domain():
         "context_id": "1",
         "cert_name": "client_example_com",
         "dkim_selector": "example_selector",
+        "dkim_private_key_file": "testdata/key",
+        "dkim_public_key_file": "testdata/public",
         "platform": "pf1"
     }
 
@@ -29,6 +31,8 @@ def relay_client_domain():
         "context_id": "1",
         "cert_name": "client_example_com",
         "dkim_selector": "example_selector",
+        "dkim_private_key_file": "testdata/key",
+        "dkim_public_key_file": "testdata/public",
         "platform": "pf2"
     } 
 
@@ -42,6 +46,8 @@ def raw_client_domain():
         "context_id": "1",
         "cert_name": "client_example_com",
         "dkim_selector": "example_selector",
+        "dkim_private_key_file": "testdata/key",
+        "dkim_public_key_file": "testdata/public",
         "platform": "pf1"
     }
 
@@ -98,13 +104,14 @@ def test_mail_domain(mailbox_client_domain, platforms, masters, pf1, pf2):
         'context_id': '1',
         'delivery': 'virtual',
         'dkim_selector': 'example_selector',
+        "dkim_private_key_file": "testdata/key",
+        "dkim_public_key_file": "testdata/public",
         'features': ['mailbox', 'mx', 'webmail'],
         'imap_domain': 'imap.mail.test.example.com',
         'name': 'mail.test.example.com',
         'ox_cluster': 'oximap',
         'smtp_domain': 'smtp.mail.test.example.com',
         'webmail_domain': 'webmail.mail.test.example.com',
-        'groupvars_platform': pf1,
     }
 
     mail_domain_not_on_target_pf = compute_mail_domain.compute_mail_domain(mailbox_client_domain, platforms, masters, pf2)
@@ -118,6 +125,8 @@ def test_relay_domain(relay_client_domain, platforms, masters, pf1, pf2):
         'context_id': '1',
         'delivery': 'relay',
         'dkim_selector': 'example_selector',
+        "dkim_private_key_file": "testdata/key",
+        "dkim_public_key_file": "testdata/public",
         'features': ['mailbox', 'mx', 'webmail'],
         'imap_domain': 'imap.mail.test.example.com',
         'name': 'mail.test.example.com',
@@ -125,7 +134,6 @@ def test_relay_domain(relay_client_domain, platforms, masters, pf1, pf2):
         'transport': 'mx.example.com',
         'smtp_domain': 'smtp.mail.test.example.com',
         'webmail_domain': 'webmail.mail.test.example.com',
-        'groupvars_platform': 'pf2',
     }
 
     mail_domain_not_on_target_pf = compute_mail_domain.compute_mail_domain(relay_client_domain, platforms, masters, pf1)
@@ -143,6 +151,8 @@ def test_proxy_to_domain(mailbox_client_domain, platforms, masters, pf1, pf2):
         'delivery': 'relay',
         'features': ['mailbox', 'mx', 'webmail'],
         'dkim_selector': 'example_selector',
+        "dkim_private_key_file": "testdata/key",
+        "dkim_public_key_file": "testdata/public",
         'imap_domain': 'imap.mail.test.example.com', # FIXME Added
         'smtp_domain': 'smtp.mail.test.example.com', # FIXME Added
         'cert_name': 'client_example_com',
@@ -150,13 +160,14 @@ def test_proxy_to_domain(mailbox_client_domain, platforms, masters, pf1, pf2):
         'ox_cluster': 'oximap',
         'context_id': '1',
         'webmail_domain': 'webmail.mail.test.example.com',
-        'groupvars_platform': pf1,
     }
     assert to_platform == {
         'name': 'mail.test.example.com',
         'delivery': 'virtual',
         'features': ['mailbox', 'mx', 'webmail'],
         'dkim_selector': 'example_selector',
+        "dkim_private_key_file": "testdata/key",
+        "dkim_public_key_file": "testdata/public",
         'imap_domain': 'imap.mail.test.example.com',
         'smtp_domain': 'smtp.mail.test.example.com',
         'cert_name': 'client_example_com',
@@ -168,7 +179,6 @@ def test_proxy_to_domain(mailbox_client_domain, platforms, masters, pf1, pf2):
             'master': 'pf1_master_user',
             'pass': 'pf1_master_pass'
         },
-        'groupvars_platform': pf1,
     }
 
 def test_proxy_from_domain(mailbox_client_domain, platforms, masters, pf1, pf2):
@@ -185,18 +195,21 @@ def test_proxy_from_domain(mailbox_client_domain, platforms, masters, pf1, pf2):
         'dkim_selector': 'example_selector',
         'imap_domain': 'imap.mail.test.example.com', # FIXME Added
         'smtp_domain': 'smtp.mail.test.example.com', # FIXME Added
+        "dkim_private_key_file": "testdata/key",
+        "dkim_public_key_file": "testdata/public",
         'cert_name': 'client_example_com',
         'transport': 'smtp:[smtp.pf2.tech.example.com]',
         'ox_cluster': 'single-server',
         'context_id': '2',
         'webmail_domain': 'webmail.mail.test.example.com',
-        'groupvars_platform': pf2,
     }
     assert to_platform == { # pf2
         'name': 'mail.test.example.com',
         'delivery': 'virtual',
         'features': ['mailbox', 'mx', 'webmail'],
         'dkim_selector': 'example_selector',
+        "dkim_private_key_file": "testdata/key",
+        "dkim_public_key_file": "testdata/public",
         'imap_domain': 'imap.mail.test.example.com',
         'smtp_domain': 'smtp.mail.test.example.com',
         'ox_cluster': 'oximap',
@@ -208,7 +221,6 @@ def test_proxy_from_domain(mailbox_client_domain, platforms, masters, pf1, pf2):
             'master': 'pf1_master_user',
             'pass': 'pf1_master_pass'
         },
-        'groupvars_platform': pf2,
     }
  
 def test_proxy_to_platform(mailbox_client_domain, platforms, masters, pf1, pf2):
@@ -227,6 +239,8 @@ def test_proxy_to_platform(mailbox_client_domain, platforms, masters, pf1, pf2):
         'delivery': 'relay',
         'features': ['mailbox', 'mx', 'webmail'],
         'dkim_selector': 'example_selector',
+        "dkim_private_key_file": "testdata/key",
+        "dkim_public_key_file": "testdata/public",
         'imap_domain': 'imap.mail.test.example.com', # FIXME Added
         'smtp_domain': 'smtp.mail.test.example.com', # FIXME Added
         'cert_name': 'client_example_com',
@@ -234,13 +248,14 @@ def test_proxy_to_platform(mailbox_client_domain, platforms, masters, pf1, pf2):
         'ox_cluster': 'oximap',
         'context_id': '1',
         'webmail_domain': 'webmail.mail.test.example.com',
-        'groupvars_platform': pf1,
     }
     assert to_platform == { # pf2
         'name': 'mail.test.example.com',
         'delivery': 'virtual',
         'features': ['mailbox', 'mx', 'webmail'],
         'dkim_selector': 'example_selector',
+        "dkim_private_key_file": "testdata/key",
+        "dkim_public_key_file": "testdata/public",
         'imap_domain': 'imap.mail.test.example.com',
         'smtp_domain': 'smtp.mail.test.example.com',
         'ox_cluster': 'single-server',
@@ -252,7 +267,6 @@ def test_proxy_to_platform(mailbox_client_domain, platforms, masters, pf1, pf2):
             'master': 'pf1_master_user',
             'pass': 'pf1_master_pass'
         },
-        'groupvars_platform': pf1,
     }
 
 def test_proxy_from_platform(mailbox_client_domain, platforms, masters, pf1, pf2):
@@ -271,6 +285,8 @@ def test_proxy_from_platform(mailbox_client_domain, platforms, masters, pf1, pf2
         'delivery': 'relay',
         'features': ['mailbox', 'mx', 'webmail'],
         'dkim_selector': 'example_selector',
+        "dkim_private_key_file": "testdata/key",
+        "dkim_public_key_file": "testdata/public",
         'imap_domain': 'imap.mail.test.example.com', # FIXME Added
         'smtp_domain': 'smtp.mail.test.example.com', # FIXME Added
         'transport': 'smtp:[smtp.pf2.tech.example.com]',
@@ -278,13 +294,14 @@ def test_proxy_from_platform(mailbox_client_domain, platforms, masters, pf1, pf2
         'context_id': '2',
         'webmail_domain': 'webmail.mail.test.example.com',
         'cert_name': 'client_example_com',
-        'groupvars_platform': pf2,
     }
     assert to_platform == { # pf2
         'name': 'mail.test.example.com',
         'delivery': 'virtual',
         'features': ['mailbox', 'mx', 'webmail'],
         'dkim_selector': 'example_selector',
+        "dkim_private_key_file": "testdata/key",
+        "dkim_public_key_file": "testdata/public",
         'imap_domain': 'imap.mail.test.example.com',
         'smtp_domain': 'smtp.mail.test.example.com',
         'ox_cluster': 'oximap',
@@ -296,7 +313,6 @@ def test_proxy_from_platform(mailbox_client_domain, platforms, masters, pf1, pf2
             'master': 'pf1_master_user',
             'pass': 'pf1_master_pass',
         },
-        'groupvars_platform': pf2,
     }
 
 def test_raw_mail_domain_rejection(raw_client_domain, platforms, masters, pf1):
diff --git a/40_ansible/group_vars/all/mysql.yml b/40_ansible/group_vars/all/mysql.yml
index 3ac316f83bb93bc959810ba277ebfea3a9d79357..250931897d1ede19bacd559305585813a4b70018 100644
--- a/40_ansible/group_vars/all/mysql.yml
+++ b/40_ansible/group_vars/all/mysql.yml
@@ -4,4 +4,3 @@ mysql_ignore_db:
  - sys
  - information_schema
  - performance_schema
-
diff --git a/40_ansible/group_vars/sql_master/main.yml b/40_ansible/group_vars/sql_master/main.yml
index e1f32b80bd55ac94570af0f5a1762559cb54be36..2f284a4d342c127c7ba95b652c840261a5da449f 100644
--- a/40_ansible/group_vars/sql_master/main.yml
+++ b/40_ansible/group_vars/sql_master/main.yml
@@ -1,3 +1,2 @@
 ---
 sql_server_id: "{{ openstack.metadata.sql_server_id }}"
-
diff --git a/40_ansible/roles/api_admin/tasks/main.yml b/40_ansible/roles/api_admin/tasks/main.yml
index acd84718a627d592677391d1612406229549d57c..c8877645e265f6755d46cda8929e97f35d7363bf 100644
--- a/40_ansible/roles/api_admin/tasks/main.yml
+++ b/40_ansible/roles/api_admin/tasks/main.yml
@@ -1,8 +1,8 @@
 ---
-- name: Create api first admin 
+- name: Create api first admin
   run_once: true
   ansible.builtin.uri:
-    url: https://api.{{ host_domain.name }}/users/ 
+    url: https://api.{{ host_domain.name }}/users/
     method: POST
     body:
       name: "{{ api_password[env_name].user }}"
@@ -13,10 +13,10 @@
     body_format: json
     user: panpan_est_le_plus_beau
     password: coincoin_est_un_serieux_concurrent
-  register: api_admin_result  
+  register: api_admin_result
   failed_when: api_admin_result.status not in [201, 403]
   changed_when: api_admin_result.status == 201
-  until: api_admin_result.status in [201, 403] 
+  until: api_admin_result.status in [201, 403]
   retries: 4
   delay: 3
-  tags: [api,apiadmin]
+  tags: [api, apiadmin]
diff --git a/40_ansible/roles/api_server/tasks/podman.yml b/40_ansible/roles/api_server/tasks/podman.yml
index 17eed92f9d1dca1b715946ba6891578ae6b4ec37..989396fdd7edd5c7e56bdbd7ba7e9c65d0960b51 100644
--- a/40_ansible/roles/api_server/tasks/podman.yml
+++ b/40_ansible/roles/api_server/tasks/podman.yml
@@ -79,7 +79,7 @@
 
 - name: Show others for api
   ansible.builtin.debug:
-    msg: "{{ [ api_password ] | map('compute_api_others', platforms, env_name) | first | to_json }}"
+    msg: "{{ [api_password] | map('compute_api_others', platforms, env_name) | first | to_json }}"
   tags: [never, api]
 
 - name: Install api container
@@ -111,7 +111,7 @@
         DIMAIL_REMOTE_CONFIG: "{{ ['something'] | map('compute_api_hosts', hostvars) | first }}"
         DIMAIL_TECH_DOMAIN: "{{ tech_domain.name }}"
         DIMAIL_MYSELF: "{{ env_name }}"
-        DIMAIL_OTHERS: "{{ [ api_password ] | map('compute_api_others', platforms, env_name) | first | to_json }}"
+        DIMAIL_OTHERS: "{{ [api_password] | map('compute_api_others', platforms, env_name) | first | to_json }}"
         DIMAIL_LOG: "normal"
       publish:
         - "8000:8000"
diff --git a/40_ansible/roles/api_server/tasks/scripts.yml b/40_ansible/roles/api_server/tasks/scripts.yml
index 95fefb305a5ec81647b19d0a7e4b4cd2cee3f7e3..74e1c6fccf22fdf4c5905deca9b9ac6aac21f252 100644
--- a/40_ansible/roles/api_server/tasks/scripts.yml
+++ b/40_ansible/roles/api_server/tasks/scripts.yml
@@ -7,4 +7,3 @@
     group: root
     mode: u=rx,g=rx,o=rx
   tags: [api]
-
diff --git a/40_ansible/roles/api_slave/tasks/main.yml b/40_ansible/roles/api_slave/tasks/main.yml
index d9d29cdac4cee1e1edb2851cfee7e4149cbe45a6..43ad2c831ed733e85f7da87b0fd1f17268b5bd72 100644
--- a/40_ansible/roles/api_slave/tasks/main.yml
+++ b/40_ansible/roles/api_slave/tasks/main.yml
@@ -67,7 +67,7 @@
     name: hash the postfix certs map
     weekday: 1
     hour: 8
-    minute: 10 
+    minute: 10
     job: /usr/sbin/postmap -o -F /opt/certs/config/postfix/certs-api; /usr/sbin/service postfix reload
     cron_file: postmap-api-certs
     user: root
diff --git a/40_ansible/roles/check_env/tasks/main.yml b/40_ansible/roles/check_env/tasks/main.yml
index 3b1079eedf9dd2548e51eda5b9b23facfaefa24b..ee05207ae4b9847279341963c081ccdf8b90ef07 100644
--- a/40_ansible/roles/check_env/tasks/main.yml
+++ b/40_ansible/roles/check_env/tasks/main.yml
@@ -16,5 +16,3 @@
       - "'webfront' in groups and groups['webfront'] | length > 0"
   when: "'api_server' in groups and groups['api_server'] | length > 0"
   tags: [check, check_env]
-
-
diff --git a/40_ansible/roles/check_vars/tasks/check_var.yml b/40_ansible/roles/check_vars/tasks/check_var.yml
index d10ed6fa48590087c75ebad779162561f88b261d..1a6d744e94b4b9b0782fbaf7fca8ca34afcb178a 100644
--- a/40_ansible/roles/check_vars/tasks/check_var.yml
+++ b/40_ansible/roles/check_vars/tasks/check_var.yml
@@ -1,6 +1,6 @@
 ---
 - name: Effectue le checkup complet pour la variable `{{ var.name }}`
-  tags: [ check, check_vars ]
+  tags: [check, check_vars]
   when: var.when | default(true)
   block:
     - name: Récupère le chemin de définition de schema de la variable `{{ var.name }}`
diff --git a/40_ansible/roles/check_vars/tasks/main.yml b/40_ansible/roles/check_vars/tasks/main.yml
index 9796139da323f52808b4b71bc8bc6759600fee1f..bd365f4d18524b89555dd8004995c35fecc310ea 100644
--- a/40_ansible/roles/check_vars/tasks/main.yml
+++ b/40_ansible/roles/check_vars/tasks/main.yml
@@ -16,7 +16,7 @@
   loop_control:
     loop_var: var
     label: "Variable {{ var.name }}"
-  tags: [ check, check_vars, always ]
+  tags: [check, check_vars, always]
 
 - name: Vérifie les variables OIDC/Keycloak si nécessaire
   ansible.builtin.include_tasks: ./check_var.yml
@@ -26,7 +26,7 @@
   loop_control:
     loop_var: var
     label: "Variable {{ var.name }}"
-  tags: [ check, check_vars, always ]
+  tags: [check, check_vars, always]
   when: "'keycloak' in groups and groups['keycloak'] | length > 0"
 
 - name: Vérifie les variables rclone pour les sauvegardes S3
@@ -41,9 +41,10 @@
   loop_control:
     loop_var: var
     label: "Variable {{ var.name }}"
-  tags: [ check, check_vars, always ]
+  tags: [check, check_vars, always]
 
 - name: Vérifie les credentials de l'API
+  tags: [check, check_vars, always]
   block:
     - name: "Vérifie la présence des credentials API pour l'environnement '{{ env_name }}'"
       ansible.builtin.assert:
@@ -52,4 +53,3 @@
           - "api_password[env_name].user is defined"
           - "api_password[env_name].password is defined"
         fail_msg: "Les credentials de l'API pour '{{ env_name }}' ne sont pas où sont incorrectement définis dans api_password !"
-  tags: [ check, check_vars, always ]
diff --git a/40_ansible/roles/cyberwatch/tasks/create_user.yml b/40_ansible/roles/cyberwatch/tasks/create_user.yml
index 768365264953763c5218919f42009ac35f434c2f..18b1527329cbcd85ded6da6d585cf48b9125a637 100644
--- a/40_ansible/roles/cyberwatch/tasks/create_user.yml
+++ b/40_ansible/roles/cyberwatch/tasks/create_user.yml
@@ -12,7 +12,7 @@
   register: cyberwatch_user
   tags: [cyberwatch, run_cyberwatch]
 
-- name: Show the user
+- name: Show the user # noqa: no-handler
   ansible.builtin.debug:
     msg: "{{ cyberwatch_user }}"
   tags: [cyberwatch, run_cyberwatch, never]
diff --git a/40_ansible/roles/cyberwatch/tasks/install_scripts.yml b/40_ansible/roles/cyberwatch/tasks/install_scripts.yml
index 3632136adebfe2e626c81ab4b73b55d56b150e90..d491d3996948cffffcf0020121e5a512478462bc 100644
--- a/40_ansible/roles/cyberwatch/tasks/install_scripts.yml
+++ b/40_ansible/roles/cyberwatch/tasks/install_scripts.yml
@@ -39,4 +39,4 @@
     - { name: "Exécution des scripts Cyberwatch",
         job: "cd /home/cyberwatch/scripts/ && bash /home/cyberwatch/scripts/run_cyberwatch.sh;" }
     - { name: "Nettoyage des vieilles archives des scripts Cyberwatch",
-        job: 'find . -type f -name "{{ archive_name_prefix }}*" -mtime +120 -exec rm {} +' }
\ No newline at end of file
+        job: 'find . -type f -name "{{ archive_name_prefix }}*" -mtime +120 -exec rm {} +' }
diff --git a/40_ansible/roles/cyberwatch/tasks/launch_and_fetch_results.yml b/40_ansible/roles/cyberwatch/tasks/launch_and_fetch_results.yml
index 381bff5ad514f0fa87cfabc30f8ce26a5ff4257e..add4d2ddaa0f6b754f5514efe065e9af0f8b41e2 100644
--- a/40_ansible/roles/cyberwatch/tasks/launch_and_fetch_results.yml
+++ b/40_ansible/roles/cyberwatch/tasks/launch_and_fetch_results.yml
@@ -3,6 +3,7 @@
   ansible.builtin.command:
     cmd: "/home/cyberwatch/scripts/run_cyberwatch.sh"
     chdir: "/home/cyberwatch/scripts/"
+  changed_when: true
   tags: [never, run_cyberwatch]
 
 - name: Cherche les archives à récupérer
diff --git a/40_ansible/roles/db_server/tasks/get_master_data.yml b/40_ansible/roles/db_server/tasks/get_master_data.yml
index 2adebcc0ee1c72fc5be66e4e91a712231c7383d2..6d61dccc6aed887ef2539cc766e8f65f6ebc33f4 100644
--- a/40_ansible/roles/db_server/tasks/get_master_data.yml
+++ b/40_ansible/roles/db_server/tasks/get_master_data.yml
@@ -103,7 +103,7 @@
       ansible.builtin.fetch:
         src: /tmp/dump.sql
         dest: /tmp/fetched/dump.sql
-        flat: yes
+        flat: true
       delegate_to: "{{ groups['sql_master'].0 }}"
 
     - name: Pull the dump to replica from S3
diff --git a/40_ansible/roles/handlers/handlers/main.yml b/40_ansible/roles/handlers/handlers/main.yml
index ff95eccdbe861c6c3bffe10d7afacf4f0adf60b9..458994af209866d2cdf8356f2d3da7454fd0b829 100644
--- a/40_ansible/roles/handlers/handlers/main.yml
+++ b/40_ansible/roles/handlers/handlers/main.yml
@@ -177,5 +177,3 @@
     name: postsrsd
     state: restarted
     enabled: true
-
-
diff --git a/40_ansible/roles/imap_server/tasks/packages.yml b/40_ansible/roles/imap_server/tasks/packages.yml
index 4a9be403de6d79777d2f1d3a14260c9a518adad8..d13f153d0dfbad27ac5395a53a4658562682484b 100644
--- a/40_ansible/roles/imap_server/tasks/packages.yml
+++ b/40_ansible/roles/imap_server/tasks/packages.yml
@@ -23,5 +23,3 @@
   notify:
     - Restart dovecot
   tags: [imap]
-
-
diff --git a/40_ansible/roles/imap_server/tasks/setup_db.yml b/40_ansible/roles/imap_server/tasks/setup_db.yml
index 1ca51e18a88c6b6d8ce17e14a4df9adfb7aeff46..aab9d6bb88e21acd2b039d4cd4c1bd48a1a508e3 100644
--- a/40_ansible/roles/imap_server/tasks/setup_db.yml
+++ b/40_ansible/roles/imap_server/tasks/setup_db.yml
@@ -82,57 +82,6 @@
         update_query: "ALTER TABLE users ADD COLUMN proxy CHAR(1) NOT NULL DEFAULT 'N'"
   tags: [imap, imapdb]
 
-# - name: Compute the proxy domains informations # noqa fqcn[action-core]
-#   set_fact:
-#     proxies:
-#       domain: "{{ mail_domains | selectattr('proxy', 'defined') | map(attribute='name') | map('regex_replace', '(.*)', \"'\\1\") }}"
-#       host: "{{ mail_domains | selectattr('proxy', 'defined') | map(attribute='proxy') | map(attribute='host') | map('regex_replace', '(.*)', \"'\\1\") }}"
-#       master: "{{ mail_domains | selectattr('proxy', 'defined') | map(attribute='proxy') | map(attribute='master') | map('regex_replace', '(.*)', \"'\\1\") }}"
-#       pass: "{{ mail_domains | selectattr('proxy', 'defined') | map(attribute='proxy') | map(attribute='pass') | map('regex_replace', '(.*)', \"'\\1\") }}"
-#       count: "{{ mail_domains | selectattr('proxy', 'defined') | map(attribute='name') | length }}"
-#   tags: [imap, imapdb]
-# 
-# - name: Compute the proxy domains query for cleaning # noqa fqcn[action-core]
-#   set_fact:
-#     proxy_sql_clean:
-#       - name: delete domain proxies we don't need anymore
-#         need_query: "select count(*) > 0 as update_needed from domain_proxy where domain not in ({{ proxies.domain | join(',') }})"
-#         update_query: "delete from domain_proxy where domain not in ({{ proxies.domain | join(',') }})"
-#   when: proxies.count|int > 0
-#   tags: [imap, imapdb]
-# 
-# - name: Compute the proxy domains query for cleaning # noqa fqcn[action-core]
-#   set_fact:
-#     proxy_sql_clean:
-#       - name: delete all domain proxies as we don't need anyone
-#         need_query: "select count(*) > 0 as update_needed from domain_proxy"
-#         update_query: "delete from domain_proxy"
-#   when: proxies.count|int == 0
-#   tags: [imap, imapdb]
-# 
-# - name: Empty the proxy_domain_queries # noqa fqcn[action-core]
-#   set_fact:
-#     proxy_sql_insert_or_update: []
-#   tags: [imap, imapdb]
-# 
-# - name: Build the proxy domains queries # noqa fqcn[action-core]
-#   set_fact:
-#     proxy_sql_insert_or_update: "{{ proxy_sql_insert_or_update + [{
-#       'name': 'ensure domain ' + proxies.domain[idx] + ' proxy configuration',
-#       'need_query': 'SELECT count(*)=0 as update_needed FROM domain_proxy WHERE domain=' +
-#          proxies.domain[idx] + ' AND host=' + proxies.host[idx] + ' AND master=' +
-#          proxies.master[idx] + ' AND pass=' + proxies.pass[idx],
-#       'update_query': 'INSERT INTO domain_proxy (domain, host, master, pass) VALUES (' +
-#          proxies.domain[idx] + ',' + proxies.host[idx] + ',' + proxies.master[idx] + ',' +
-#          proxies.pass[idx] + ') ON DUPLICATE KEY UPDATE host=' + proxies.host[idx] + ', master=' +
-#          proxies.master[idx] + ', pass=' + proxies.pass[idx]
-#     }] }}"
-#   loop: "{{ proxies.domain }}"
-#   loop_control:
-#     loop_var: better_than_empty
-#     index_var: idx
-#   tags: [imap, imapdb]
-# 
 - name: Create table 'domain_proxy' in 'dovecot' database
   ansible.builtin.include_role:
     name: setup_db_table
diff --git a/40_ansible/roles/keycloak_server/tasks/install.yml b/40_ansible/roles/keycloak_server/tasks/install.yml
index f82385e40fa8652bd5972f05bef465e971f7a1e8..16643c1fb4e800bad57048d17c4c8e01903537dc 100644
--- a/40_ansible/roles/keycloak_server/tasks/install.yml
+++ b/40_ansible/roles/keycloak_server/tasks/install.yml
@@ -9,6 +9,7 @@
 - name: Ensure /home/keycloak/keycloak.keystore is available
   ansible.builtin.stat:
     path: /home/keycloak/keycloak.keystore
+  become: true
   become_user: keycloak
   register: keystore_file
   tags: [keycloak]
diff --git a/40_ansible/roles/mail_filter/tasks/amavis.yml b/40_ansible/roles/mail_filter/tasks/amavis.yml
index 677d17139a04d33e1bba41ba5b1db42b3b760508..35375327411467c9a33979b1eb6bd7ab5cddae00 100644
--- a/40_ansible/roles/mail_filter/tasks/amavis.yml
+++ b/40_ansible/roles/mail_filter/tasks/amavis.yml
@@ -8,4 +8,3 @@
     group: root
   notify: Restart amavis
   tags: [spam]
-
diff --git a/40_ansible/roles/mail_filter/tasks/clamav.yml b/40_ansible/roles/mail_filter/tasks/clamav.yml
index 838a2836ae8f7bcf8ffcea3b8dfdcb4bc946031b..a30a8da369fc1a5d2ded9f1806623e3960a5992c 100644
--- a/40_ansible/roles/mail_filter/tasks/clamav.yml
+++ b/40_ansible/roles/mail_filter/tasks/clamav.yml
@@ -5,5 +5,3 @@
     groups: amavis
     append: true
   tags: [spam]
-
-
diff --git a/40_ansible/roles/mail_filter/tasks/main.yml b/40_ansible/roles/mail_filter/tasks/main.yml
index e820bc9d4a350dc579628537e76e6413c7e47a38..3e8675fb6a43060910e7caead49e2fef1df6533e 100644
--- a/40_ansible/roles/mail_filter/tasks/main.yml
+++ b/40_ansible/roles/mail_filter/tasks/main.yml
@@ -7,17 +7,3 @@
     - clamav.yml
     - amavis.yml
   tags: [spam]
-
-#- name: Copy the nagios service config file
-#  run_once: true
-#  ansible.builtin.template:
-#    src: nagios_smtp.cfg.j2
-#    dest: /etc/nagios4/conf.d/services/smtp.cfg
-#    mode: u=rw,g=r,o=r
-#    owner: nagios
-#    group: nagios
-#  delegate_to: "{{ groups['monitor'].0 }}"
-#  tags: [monitor, monitorsmtp, smtp]
-#  notify:
-#    - Reload nagios
-#
diff --git a/40_ansible/roles/mail_filter/tasks/packages.yml b/40_ansible/roles/mail_filter/tasks/packages.yml
index 37f42b208c1dd177283e868fea31bb088bb80be8..7b5f1b53018d1b42a542ff897609741a5b878aad 100644
--- a/40_ansible/roles/mail_filter/tasks/packages.yml
+++ b/40_ansible/roles/mail_filter/tasks/packages.yml
@@ -7,4 +7,3 @@
       - clamav-daemon
     state: latest
   tags: [spam]
-
diff --git a/40_ansible/roles/mail_filter/tasks/spamassassin.yml b/40_ansible/roles/mail_filter/tasks/spamassassin.yml
index 2002c9329fd9d013f393249d5e5c21daad2cc9a7..b538e01a02947cfe66b4df32f922749b772f5002 100644
--- a/40_ansible/roles/mail_filter/tasks/spamassassin.yml
+++ b/40_ansible/roles/mail_filter/tasks/spamassassin.yml
@@ -20,4 +20,3 @@
   notify:
     - Restart spamd
   tags: [spam]
-
diff --git a/40_ansible/roles/nginx_server/tasks/prometheus.yml b/40_ansible/roles/nginx_server/tasks/prometheus.yml
index 1696d74b41cfd0e8e2172186d67d7d28ee3dab33..07e08af2879c715d0a48afe1b65db3917f6b46ad 100644
--- a/40_ansible/roles/nginx_server/tasks/prometheus.yml
+++ b/40_ansible/roles/nginx_server/tasks/prometheus.yml
@@ -37,4 +37,3 @@
   tags: [always]
   retries: 2
   delay: 3
-
diff --git a/40_ansible/roles/ox_server/tasks/packages.yml b/40_ansible/roles/ox_server/tasks/packages.yml
index d12994e6efd817d1f62c2d09c224a7850b5a9cbc..5c2463fe2a05b9f91717013ff9f1d040703a8fbd 100644
--- a/40_ansible/roles/ox_server/tasks/packages.yml
+++ b/40_ansible/roles/ox_server/tasks/packages.yml
@@ -17,7 +17,7 @@
       - open-xchange-dynamic-theme={{ ox_server_version }}*
       - open-xchange-oidc={{ ox_server_version }}*
       - open-xchange-filestore-s3={{ ox_server_version }}*
-      - open-xchange-authentication-application-storage-rdb={{ox_server_version}}*
+      - open-xchange-authentication-application-storage-rdb={{ ox_server_version }}*
       - open-xchange-appsuite-help-common={{ ox_server_version }}*
       - open-xchange-appsuite-help-zh-tw={{ ox_server_version }}*
       - open-xchange-appsuite-help-zh-cn={{ ox_server_version }}*
@@ -94,7 +94,7 @@
     - "{{ dimail_ui_package_url }}"
   tags: [ox, oxplugins]
 
-- name: Copy DIMAIL favicon 
+- name: Copy DIMAIL favicon
   ansible.builtin.copy:
     src: "favicon.ico"
     dest: /opt/open-xchange/appsuite/apps/themes/icons/default/favicon.ico
diff --git a/40_ansible/roles/ox_server/tasks/restart_ox.yml b/40_ansible/roles/ox_server/tasks/restart_ox.yml
index f55527f1ac144d3cc0055b9246c582c019c12ecd..dd409e43ab572f8350272c42751694226d4d5cb7 100644
--- a/40_ansible/roles/ox_server/tasks/restart_ox.yml
+++ b/40_ansible/roles/ox_server/tasks/restart_ox.yml
@@ -1,4 +1,4 @@
-- name: ensure openXchange log file exists
+- name: Ensure openXchange log file exists
   ansible.builtin.copy:
     content: ""
     dest: /var/log/mail/open-xchange/open-xchange.log.0
@@ -11,6 +11,7 @@
 - name: Get the current size of the openXchange log file
   ansible.builtin.command: stat --format=%s /var/log/mail/open-xchange/open-xchange.log.0
   register: log_size_before
+  changed_when: false
   tags: [ox]
 
 - name: Restart the service
@@ -21,7 +22,11 @@
 
 - name: Wait for the startup magic line
   ansible.builtin.shell: >
-    timeout 300 tail -c +{{ log_size_before.stdout | int + 1 }} -F /var/log/mail/open-xchange/open-xchange.log.0 | grep -m 1 "The server should be up and running..."
+    set -o pipefail && \
+    timeout 300 tail -c +{{ log_size_before.stdout | int + 1 }} \
+      -F /var/log/mail/open-xchange/open-xchange.log.0 | grep \
+      -m 1 "The server should be up and running..."
   register: tail_result
+  change_when: true
   failed_when: tail_result.rc != 0
   tags: [ox]
diff --git a/40_ansible/roles/ox_static/tasks/main.yml b/40_ansible/roles/ox_static/tasks/main.yml
index 9c5eb367cd5669ef53fd4a5c42196e649b1768c4..5ea2954bf15ee42cc288dc8441e6fda5816cd32d 100644
--- a/40_ansible/roles/ox_static/tasks/main.yml
+++ b/40_ansible/roles/ox_static/tasks/main.yml
@@ -22,5 +22,3 @@
     state: latest
     cache_valid_time: 600 # = 10 minutes
   tags: [always]
-
-      
diff --git a/40_ansible/roles/secure_vms/tasks/fail2ban.yml b/40_ansible/roles/secure_vms/tasks/fail2ban.yml
index d8e6ab42d673ba56fddf90ed70e96568df65e4b6..e41363ed4a319232b48988d3bc01b314a8072503 100644
--- a/40_ansible/roles/secure_vms/tasks/fail2ban.yml
+++ b/40_ansible/roles/secure_vms/tasks/fail2ban.yml
@@ -15,4 +15,3 @@
   notify:
     - Reload fail2ban
   tags: [security, system]
-
diff --git a/40_ansible/roles/secure_vms/tasks/ssh.yml b/40_ansible/roles/secure_vms/tasks/ssh.yml
index 4bcb51c9becbccce5f897c42b28409c8480ddc79..57b2385f34e3c6d021670c96910b9520bb14d0fe 100644
--- a/40_ansible/roles/secure_vms/tasks/ssh.yml
+++ b/40_ansible/roles/secure_vms/tasks/ssh.yml
@@ -20,7 +20,6 @@
       tags: [security, system]
   rescue:
     - name: Custom error message
-      fail:
-        msg: The {{ secure_vms_sshd_config}} file should allow user 'api' to connect.
+      ansible.builtin.fail:
+        msg: The {{ secure_vms_sshd_config }} file should allow user 'api' to connect.
       tags: [security, system]
-
diff --git a/40_ansible/roles/setup_container/tasks/main.yml b/40_ansible/roles/setup_container/tasks/main.yml
index ae584a2924611b9681256177436f65e008c0f8d9..0a920b3862c2f19a42652aa22814cfe3dd10a93f 100644
--- a/40_ansible/roles/setup_container/tasks/main.yml
+++ b/40_ansible/roles/setup_container/tasks/main.yml
@@ -37,7 +37,7 @@
       become_user: "{{ username }}"
       tags: [always]
 
-    - name: Setup the podman container for {{ container.name }}
+    - name: Setup the podman container for {{ container.name }} # noqa: args[module]
       containers.podman.podman_container: "{{ container | combine(mandatory_podman_config) }}"
       become: true
       become_user: "{{ username }}"
diff --git a/40_ansible/roles/setup_kind/tasks/setup_ox8.yml b/40_ansible/roles/setup_kind/tasks/setup_ox8.yml
index 78c48eeaa9ba1b79c978a10f04f23bdda395bfdb..410e1a7e2d400374e610fb0f8c47cb8e6ef43bb7 100644
--- a/40_ansible/roles/setup_kind/tasks/setup_ox8.yml
+++ b/40_ansible/roles/setup_kind/tasks/setup_ox8.yml
@@ -1,5 +1,5 @@
 ---
-- name: Download OX8 charts 
+- name: Download OX8 charts
   ansible.builtin.unarchive:
     src: https://gitlab.open-xchange.com/appsuite/operation-guides/-/archive/main/operation-guides-main.tar.gz
     dest: "/tmp"
@@ -28,7 +28,7 @@
   kubernetes.core.helm:
     name: as8
     chart_ref: oci://registry.open-xchange.com/appsuite/charts/appsuite
-    chart_version: 8.33.241 
+    chart_version: 8.33.241
     release_namespace: as8
     values_files:
       - /tmp/values.yaml
diff --git a/40_ansible/roles/setup_system/tasks/bashrc.yml b/40_ansible/roles/setup_system/tasks/bashrc.yml
index fed4b94af2eb9849beaba33b7edf4fee8d787101..ac9e61cec1ff567f95f87b112e9ced0965823fd4 100644
--- a/40_ansible/roles/setup_system/tasks/bashrc.yml
+++ b/40_ansible/roles/setup_system/tasks/bashrc.yml
@@ -17,5 +17,3 @@
     owner: root
     group: root
   tags: [system, home]
-
-
diff --git a/40_ansible/roles/setup_system/tasks/hostname.yml b/40_ansible/roles/setup_system/tasks/hostname.yml
index 8c3fef5682bca80a7a3e9b651a47b696aa089770..31100fe4267fd26c24245a9a99d413a7d130eaca 100644
--- a/40_ansible/roles/setup_system/tasks/hostname.yml
+++ b/40_ansible/roles/setup_system/tasks/hostname.yml
@@ -12,4 +12,3 @@
     owner: root
     group: root
   tags: [system]
-
diff --git a/40_ansible/roles/setup_system/tasks/upgrade.yml b/40_ansible/roles/setup_system/tasks/upgrade.yml
index e0ff14b56927049e28b48e264812aaf22f5fbb12..2ff64b288daf273001e43a961b858d7b851d7480 100644
--- a/40_ansible/roles/setup_system/tasks/upgrade.yml
+++ b/40_ansible/roles/setup_system/tasks/upgrade.yml
@@ -20,5 +20,3 @@
     name: unattended-upgrades
     state: stopped
   tags: [system]
-
-
diff --git a/40_ansible/roles/setup_system/tasks/vimrc.yml b/40_ansible/roles/setup_system/tasks/vimrc.yml
index dc2b7fbfd1958f8645013a2463bd18a3f84d17c8..3edd1a49680d18bcad3874cd2ae6baf0147d1b58 100644
--- a/40_ansible/roles/setup_system/tasks/vimrc.yml
+++ b/40_ansible/roles/setup_system/tasks/vimrc.yml
@@ -17,4 +17,3 @@
     owner: "{{ default_user }}"
     group: "{{ default_user }}"
   tags: [system, home]
-
diff --git a/40_ansible/roles/setup_user/tasks/main.yml b/40_ansible/roles/setup_user/tasks/main.yml
index d845240400969c570b0ea3c46406da4268730fa1..4f4395d60fe079a1806888410ef4f4b885d4749b 100644
--- a/40_ansible/roles/setup_user/tasks/main.yml
+++ b/40_ansible/roles/setup_user/tasks/main.yml
@@ -11,7 +11,7 @@
     group: "{{ system_username }}"
     uid: "{{ system_uid | default(omit) }}"
     shell: "{{ system_shell | default('/bin/bash') }}"
-    home: "{{ system_home | default(omit) }}" 
+    home: "{{ system_home | default(omit) }}"
   register: container_user
   tags: [always]
 
diff --git a/40_ansible/roles/smtp_server/tasks/packages.yml b/40_ansible/roles/smtp_server/tasks/packages.yml
index 2638e61979c38885b7247741cfe3eae0914d49d9..03c0b1a464fe0a6e560c8580683806a06a60d158 100644
--- a/40_ansible/roles/smtp_server/tasks/packages.yml
+++ b/40_ansible/roles/smtp_server/tasks/packages.yml
@@ -11,5 +11,3 @@
   notify:
     - Restart postfix
   tags: [smtp]
-
-
diff --git a/40_ansible/roles/smtp_server/tasks/setup_db_postfix.yml b/40_ansible/roles/smtp_server/tasks/setup_db_postfix.yml
index 3b1db16772878ce6d226fb3cd20c3991a8e06fd9..f1465b49d4519a22b6c479cccf940f40638b570c 100644
--- a/40_ansible/roles/smtp_server/tasks/setup_db_postfix.yml
+++ b/40_ansible/roles/smtp_server/tasks/setup_db_postfix.yml
@@ -105,8 +105,8 @@
       {
         name: "Add 'domain' column to table 'sender_login_map'",
         need_query: "
-          SELECT count(*) = 0 as update_needed 
-          FROM information_schema.columns 
+          SELECT count(*) = 0 as update_needed
+          FROM information_schema.columns
           WHERE table_name = 'sender_login_map' AND column_name = 'domain'
         ",
         update_query: "
diff --git a/40_ansible/roles/sticky_users/tasks/api.yml b/40_ansible/roles/sticky_users/tasks/api.yml
index 912beecffdcd0c58023d048f35ea467f1498135e..013c04a9ff5acff211cdc081c617e1dccf21f265 100644
--- a/40_ansible/roles/sticky_users/tasks/api.yml
+++ b/40_ansible/roles/sticky_users/tasks/api.yml
@@ -5,7 +5,7 @@
   register: api_data_file
   tags: [sticky_users, system]
 
-- name: create api user if needed
+- name: Create api user if needed
   ansible.builtin.include_role:
     name: setup_user
   vars:
diff --git a/40_ansible/roles/sticky_users/tasks/dovecot.yml b/40_ansible/roles/sticky_users/tasks/dovecot.yml
index e8d0567246e794a19f96cb519b02bebd4925502b..8d25a800ed47742bc0a63fc5fadac36372b98b5b 100644
--- a/40_ansible/roles/sticky_users/tasks/dovecot.yml
+++ b/40_ansible/roles/sticky_users/tasks/dovecot.yml
@@ -5,7 +5,7 @@
   register: dovenull_data_file
   tags: [sticky_users, system]
 
-- name: create dovenull user if needed
+- name: Create dovenull user if needed
   ansible.builtin.include_role:
     name: setup_user
   vars:
diff --git a/40_ansible/roles/sticky_users/tasks/mysql.yml b/40_ansible/roles/sticky_users/tasks/mysql.yml
index 605128757c1be695b1f3bb6b5665df0f98c7d407..18ebdb9b7c4207c936f99fc540ad8e7d1c462d5d 100644
--- a/40_ansible/roles/sticky_users/tasks/mysql.yml
+++ b/40_ansible/roles/sticky_users/tasks/mysql.yml
@@ -5,10 +5,10 @@
   register: mysql_data_file
   tags: [sticky_users, system]
 
-- name: create mysql user if needed
+- name: Create mysql user if needed
   ansible.builtin.include_role:
     name: setup_user
-  vars: 
+  vars:
     system_username: "mysql"
     system_uid: "{{ mysql_data_file.stat.uid | int }}"
     system_gid: "{{ mysql_data_file.stat.gid | int }}"
@@ -17,5 +17,3 @@
     system_linger: false
   when: mysql_data_file.stat.exists
   tags: [sticky_users, system]
-    
-
diff --git a/40_ansible/roles/temurin/tasks/main.yml b/40_ansible/roles/temurin/tasks/main.yml
index c7ae4088d25a485532640fa35418927d97f799f9..91203328ce5a8e32a08a7671e87976b204dded9a 100644
--- a/40_ansible/roles/temurin/tasks/main.yml
+++ b/40_ansible/roles/temurin/tasks/main.yml
@@ -7,4 +7,3 @@
   loop_control:
     loop_var: tem_task
   tags: [always]
-
diff --git a/40_ansible/roles/temurin/tasks/packages.yml b/40_ansible/roles/temurin/tasks/packages.yml
index 2ca2b86d72bff9465c0981d03df4c316066740c2..b864c09c4f07d09e2ebf80445eead057e1c6bc58 100644
--- a/40_ansible/roles/temurin/tasks/packages.yml
+++ b/40_ansible/roles/temurin/tasks/packages.yml
@@ -6,4 +6,3 @@
     state: latest
     cache_valid_time: 600 # = 10 minutes
   tags: [always]
-
diff --git a/40_ansible/roles/webfront/tasks/main.yml b/40_ansible/roles/webfront/tasks/main.yml
index 0aa03d6d4abebb6b10f379e922065b7e4acec6e5..d2bd56238c51f7e0b55d7741b6c27ed6849f8847 100644
--- a/40_ansible/roles/webfront/tasks/main.yml
+++ b/40_ansible/roles/webfront/tasks/main.yml
@@ -54,6 +54,3 @@
     may_notify:
       - Reload nginx
   tags: [webfront, api, monitor, ox, keycloak, monitorcert]
-
-
-
diff --git a/tools/nix/ansible.nix b/tools/nix/ansible.nix
new file mode 100644
index 0000000000000000000000000000000000000000..185155ca8d9fd62d530a5779fb95d87a5f7fa1c3
--- /dev/null
+++ b/tools/nix/ansible.nix
@@ -0,0 +1,14 @@
+{ pkgs }:
+[
+  pkgs.ansible
+  pkgs.ansible-lint
+  (pkgs.python3.withPackages (python-pkgs: [
+    python-pkgs.openstacksdk
+    python-pkgs.passlib
+    python-pkgs.python-ldap
+    python-pkgs.boto3
+    python-pkgs.configparser
+    python-pkgs.pyhcl
+    python-pkgs.pytest
+  ]))
+]
diff --git a/tools/nix/dev.nix b/tools/nix/dev.nix
new file mode 100644
index 0000000000000000000000000000000000000000..35a3ad011b680ef47507d59c92ee317560eca12d
--- /dev/null
+++ b/tools/nix/dev.nix
@@ -0,0 +1,7 @@
+{ pkgs }:
+[
+  pkgs.git 
+  pkgs.git-lfs
+  pkgs.nodejs
+  pkgs.nushell
+]
diff --git a/tools/nix/flake.lock b/tools/nix/flake.lock
index 28f9ede29d85a175d422177eb79448e145334a15..ef827690386c27d78244ea61e594aa6aeb686998 100644
--- a/tools/nix/flake.lock
+++ b/tools/nix/flake.lock
@@ -3,8 +3,8 @@
     "nixpkgs": {
       "locked": {
         "lastModified": 0,
-        "narHash": "sha256-D1FNZ70NmQEwNxpSSdTXCSklBH1z2isPR84J6DQrJGs=",
-        "path": "/nix/store/n1g84klfb0h3bpwyvc59lcy5ca58h36w-source",
+        "narHash": "sha256-5+Hmo4nbqw8FrW85FlNm4IIrRnZ7bn0cmXlScNsNRLo=",
+        "path": "/nix/store/p6ccgjymrlv4wwnxkx9hbhz0rbd5mnlr-source",
         "type": "path"
       },
       "original": {
@@ -14,8 +14,8 @@
     },
     "nixpkgs-2405": {
       "locked": {
-        "lastModified": 1734304571,
-        "narHash": "sha256-/SdulIgeXdXPdSY18pZE1q4wuBbk6ymQ0tnaQILD7zQ=",
+        "lastModified": 1735665511,
+        "narHash": "sha256-m6KS4Y44VAxk5ZnELE2dzLbjPtKRGtsprphQC6A7Erk=",
         "type": "tarball",
         "url": "https://nixos.org/channels/nixos-24.05/nixexprs.tar.xz"
       },
diff --git a/tools/nix/flake.nix b/tools/nix/flake.nix
index 088926fc5f0983741203dfbe22b95a9047c4a5e2..6cfd280be0c5cd7a0589c08ac2cb0078d59073b0 100644
--- a/tools/nix/flake.nix
+++ b/tools/nix/flake.nix
@@ -3,41 +3,22 @@
 
   inputs = {
     nixpkgs-2405.url = "https://nixos.org/channels/nixos-24.05/nixexprs.tar.xz";
-#    nixpkgs-terraform.url = "https://github.com/NixOS/nixpkgs/archive/4ab8a3de296914f3b631121e9ce3884f1d34e1e5.tar.gz";
   };
 
   outputs = { self, nixpkgs-2405, nixpkgs }:
   let
     pkgs = import nixpkgs { system = "x86_64-linux"; config.allowUnfree = true; };
-#    pkgs-terraform = import nixpkgs-terraform {system = "x86_64-linux"; };
-    pkgs-2405 = import nixpkgs-2405 {system = "x86_64-linux"; };
+    dev = ( import ./dev.nix { inherit pkgs; });
+    ansible = ( import ./ansible.nix { inherit pkgs; });
+    terraform = ( import ./terraform.nix { inherit pkgs; });
   in {
     devShell.x86_64-linux = pkgs.mkShell {
-      nativeBuildInputs = with pkgs-2405.buildPackages; [
-        git git-lfs
-        # pkgs.jetbrains.idea-ultimate
-        # pkgs.jetbrains.pycharm-professional
-        pkgs.vscode
-        tflint
-        pkgs.terraform
-        pkgs.nodejs
-        pkgs.nushell
-        ansible
-        ansible-lint
-        (pkgs-2405.python3.withPackages (python-pkgs: [
-          python-pkgs.openstacksdk
-          python-pkgs.passlib
-          python-pkgs.python-ldap
-          python-pkgs.boto3
-          python-pkgs.configparser
-          python-pkgs.pyhcl
-          python-pkgs.pytest
-        ]))
-      ];
+      nativeBuildInputs = dev ++ ansible ++ terraform;
       shellHook = ''
         export ANSIBLE_DISPLAY_OK_HOSTS=yes
         export PS1="\n\[\033[1;36m\][\[\e]0;\u@\h: \w\a\]\u@\h:\w]\$\[\033[0m\] "
       '';
     };
+    ansible = pkgs.mkShell { nativeBuildInputs = ansible; };
   };
 }
diff --git a/tools/nix/terraform.nix b/tools/nix/terraform.nix
new file mode 100644
index 0000000000000000000000000000000000000000..30c69c71bd48d5683ab10a2eb275326d8d66ed13
--- /dev/null
+++ b/tools/nix/terraform.nix
@@ -0,0 +1,5 @@
+{ pkgs }:
+[
+  pkgs.flint
+  pkgs.terraform
+]