Wazuh with Ansible - Automated Deployment Guide
Ansible enables automated installation and configuration of all Wazuh 4.14 components across any number of hosts. The official wazuh-ansible repository provides roles for the indexer, server, dashboard, and agents, supporting both single-node and multi-node architectures.
Prerequisites
Control node (Ansible controller)
- Ansible 2.12 or later
- Python 3.9+
- SSH access to target hosts
- sudo privileges on target hosts
Verify the Ansible version:
ansible --versionTarget hosts
- A 64-bit OS from the supported list
- Internet access for downloading Wazuh packages (or an offline repository )
- Open ports: 1514, 1515, 9200, 443, 55000
Installing the collection
Install the official Wazuh roles via Ansible Galaxy:
ansible-galaxy collection install wazuh.wazuhOr clone the repository directly:
git clone https://github.com/wazuh/wazuh-ansible.git -b v4.14.3
cd wazuh-ansibleRole structure
The wazuh.wazuh collection includes the following roles:
| Role | Purpose |
|---|---|
wazuh.wazuh.wazuh_indexer | Install and configure Wazuh Indexer (OpenSearch) |
wazuh.wazuh.wazuh_manager | Install and configure Wazuh Manager |
wazuh.wazuh.wazuh_dashboard | Install and configure Wazuh Dashboard |
wazuh.wazuh.wazuh_agent | Install and configure Wazuh Agent |
Each role ships with configuration templates, service restart handlers, and parameter validation.
Inventory
Single-node configuration
File inventory/single-node.yml:
all:
children:
wazuh_indexer:
hosts:
wazuh-node1:
ansible_host: 192.168.1.10
ansible_user: deploy
ansible_become: true
indexer_node_name: wazuh-indexer-1
wazuh_manager:
hosts:
wazuh-node1:
manager_type: master
wazuh_dashboard:
hosts:
wazuh-node1:
wazuh_agent:
hosts:
web-server-1:
ansible_host: 192.168.1.20
ansible_user: deploy
ansible_become: true
db-server-1:
ansible_host: 192.168.1.21
ansible_user: deploy
ansible_become: trueMulti-node configuration
File inventory/multi-node.yml:
all:
children:
wazuh_indexer:
hosts:
indexer-1:
ansible_host: 192.168.1.10
indexer_node_name: wazuh-indexer-1
indexer-2:
ansible_host: 192.168.1.11
indexer_node_name: wazuh-indexer-2
indexer-3:
ansible_host: 192.168.1.12
indexer_node_name: wazuh-indexer-3
vars:
ansible_user: deploy
ansible_become: true
wazuh_manager:
hosts:
manager-master:
ansible_host: 192.168.1.20
manager_type: master
manager-worker:
ansible_host: 192.168.1.21
manager_type: worker
vars:
ansible_user: deploy
ansible_become: true
wazuh_dashboard:
hosts:
dashboard-1:
ansible_host: 192.168.1.30
vars:
ansible_user: deploy
ansible_become: true
wazuh_agent:
hosts:
app-server-[1:10]:
ansible_user: deploy
ansible_become: trueSingle-node playbook
File playbooks/wazuh-single-node.yml:
- name: Install Wazuh Indexer
hosts: wazuh_indexer
roles:
- role: wazuh.wazuh.wazuh_indexer
vars:
indexer_cluster_name: wazuh-cluster
indexer_node_master: true
indexer_node_data: true
indexer_network_host: "0.0.0.0"
indexer_admin_password: "ChangeMe!SecureP@ss1"
- name: Install Wazuh Manager
hosts: wazuh_manager
roles:
- role: wazuh.wazuh.wazuh_manager
vars:
wazuh_manager_config:
cluster:
disabled: true
api:
bind_addr: "0.0.0.0"
- name: Install Wazuh Dashboard
hosts: wazuh_dashboard
roles:
- role: wazuh.wazuh.wazuh_dashboard
vars:
dashboard_server_host: "0.0.0.0"
dashboard_server_port: 443
indexer_url: "https://{{ hostvars[groups['wazuh_indexer'][0]]['ansible_host'] }}:9200"Execute the playbook:
ansible-playbook -i inventory/single-node.yml playbooks/wazuh-single-node.ymlMulti-node playbook
File playbooks/wazuh-multi-node.yml:
- name: Install Wazuh Indexer cluster
hosts: wazuh_indexer
roles:
- role: wazuh.wazuh.wazuh_indexer
vars:
indexer_cluster_name: wazuh-cluster
indexer_cluster_initial_master_nodes:
- wazuh-indexer-1
- wazuh-indexer-2
- wazuh-indexer-3
indexer_discovery_seed_hosts:
- "{{ hostvars['indexer-1']['ansible_host'] }}"
- "{{ hostvars['indexer-2']['ansible_host'] }}"
- "{{ hostvars['indexer-3']['ansible_host'] }}"
indexer_admin_password: "ChangeMe!SecureP@ss1"
- name: Install Wazuh Manager cluster
hosts: wazuh_manager
roles:
- role: wazuh.wazuh.wazuh_manager
vars:
wazuh_manager_config:
cluster:
disabled: false
name: wazuh-manager-cluster
node_name: "{{ inventory_hostname }}"
node_type: "{{ manager_type }}"
key: "ChangeThisClusterKey123"
nodes:
- "{{ hostvars['manager-master']['ansible_host'] }}"
port: 1516
bind_addr: "0.0.0.0"
hidden: false
- name: Install Wazuh Dashboard
hosts: wazuh_dashboard
roles:
- role: wazuh.wazuh.wazuh_dashboard
vars:
dashboard_server_host: "0.0.0.0"
indexer_url: "https://{{ hostvars[groups['wazuh_indexer'][0]]['ansible_host'] }}:9200"
- name: Install Wazuh Agents
hosts: wazuh_agent
roles:
- role: wazuh.wazuh.wazuh_agent
vars:
wazuh_manager_address: "{{ hostvars['manager-master']['ansible_host'] }}"
wazuh_agent_group: "default"Execute the playbook:
ansible-playbook -i inventory/multi-node.yml playbooks/wazuh-multi-node.ymlVariables reference
Indexer variables
| Variable | Description | Default |
|---|---|---|
indexer_cluster_name | Cluster name | wazuh-cluster |
indexer_node_name | Node name | wazuh-indexer-1 |
indexer_node_master | Master-eligible role | true |
indexer_node_data | Data role | true |
indexer_network_host | Bind address | 0.0.0.0 |
indexer_http_port | HTTP port | 9200 |
indexer_transport_port | Transport port | 9300 |
indexer_admin_password | Admin password | SecretPassword |
indexer_jvm_xms | JVM Heap min | 1g |
indexer_jvm_xmx | JVM Heap max | 1g |
Manager variables
| Variable | Description | Default |
|---|---|---|
wazuh_manager_config.cluster.disabled | Disable clustering | true |
wazuh_manager_config.cluster.name | Cluster name | wazuh |
wazuh_manager_config.cluster.node_type | Node type (master/worker) | master |
wazuh_manager_config.cluster.key | Cluster key | - |
wazuh_manager_config.api.bind_addr | API bind address | 0.0.0.0 |
wazuh_manager_config.api.port | API port | 55000 |
wazuh_manager_authd.enabled | Enable authd | true |
wazuh_manager_authd.use_password | Require enrollment password | false |
Agent variables
| Variable | Description | Default |
|---|---|---|
wazuh_manager_address | Manager IP or DNS | - |
wazuh_agent_group | Agent group | default |
wazuh_agent_name | Agent name | {{ inventory_hostname }} |
wazuh_agent_enrollment.enabled | Auto-enrollment | true |
wazuh_agent_enrollment.auth_pass | Enrollment password | - |
Agent enrollment via Ansible
Bulk agent deployment
To deploy agents across a host group, create a dedicated playbook:
- name: Deploy Wazuh agents
hosts: wazuh_agent
roles:
- role: wazuh.wazuh.wazuh_agent
vars:
wazuh_manager_address: 192.168.1.20
wazuh_agent_group: "{{ group_names | join(',') }}"
wazuh_agent_enrollment:
enabled: true
auth_pass: "AgentEnrollmentPassword"Password-based enrollment
Enable password-based agent authorization on the manager:
- name: Configure manager for password-based enrollment
hosts: wazuh_manager
roles:
- role: wazuh.wazuh.wazuh_manager
vars:
wazuh_manager_authd:
enabled: true
use_password: true
password: "AgentEnrollmentPassword"Verify enrollment
ansible wazuh_manager -i inventory/multi-node.yml -m shell \
-a "/var/ossec/bin/agent_control -l"Custom configuration
Deploying custom rules
- name: Deploy custom rules
hosts: wazuh_manager
tasks:
- name: Copy custom rules
copy:
src: files/local_rules.xml
dest: /var/ossec/etc/rules/local_rules.xml
owner: wazuh
group: wazuh
mode: "0640"
notify: restart wazuh-manager
handlers:
- name: restart wazuh-manager
service:
name: wazuh-manager
state: restartedConfiguring integrations
- name: Configure Slack integration
hosts: wazuh_manager
roles:
- role: wazuh.wazuh.wazuh_manager
vars:
wazuh_manager_config:
integration:
- name: slack
hook_url: "https://hooks.slack.com/services/xxx/yyy/zzz"
level: 10
alert_format: jsonTroubleshooting
SSH connection failures
Symptoms: Ansible cannot connect to the target host.
Solution:
- Test SSH access manually:
ssh deploy@192.168.1.10Verify that the SSH key is present in
authorized_keyson the target hostCheck the
ansible.cfgconfiguration:
[defaults]
host_key_checking = False
timeout = 30Package installation failures
Symptoms: the role fails during Wazuh package installation.
Solution:
- Verify Wazuh repository availability from the target host:
curl -s https://packages.wazuh.com/4.x/apt/ | head -5Confirm that the repository GPG key is installed
For air-gapped networks, configure an offline repository
Indexer cluster fails to form
Symptoms: indexer nodes start but do not join the cluster.
Solution:
Verify that
indexer_discovery_seed_hostscontains all node IP addressesConfirm that port 9300 is open between nodes
Ensure
indexer_cluster_initial_master_nodeslists all node names
Agent fails to register
Symptoms: the agent is installed but does not appear in the agent list.
Solution:
- Test manager reachability from the agent host:
telnet 192.168.1.20 1515If password-based enrollment is enabled, verify that passwords match on the manager and agent
Check agent logs:
tail -50 /var/ossec/logs/ossec.log