# Wazuh with Ansible - Automated Deployment Guide

> Deploy Wazuh 4.14 using Ansible - official roles, single-node and multi-node playbooks, agent enrollment automation, and a complete variables reference

Source: https://opennix.org/en/docs/wazuh/deployment/wazuh-ansible-deployment/


Ansible enables automated installation and configuration of all Wazuh 4.14 components across any number of hosts. The official [wazuh-ansible](https://github.com/wazuh/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:

```bash
ansible --version
```

### Target hosts

- A 64-bit OS from the [supported list](/docs/wazuh/installation/)
- Internet access for downloading Wazuh packages (or an [offline repository](/docs/wazuh/deployment/wazuh-offline-installation/))
- Open ports: 1514, 1515, 9200, 443, 55000

### Installing the collection

Install the official Wazuh roles via Ansible Galaxy:

```bash
ansible-galaxy collection install wazuh.wazuh
```

Or clone the repository directly:

```bash
git clone https://github.com/wazuh/wazuh-ansible.git -b v4.14.3
cd wazuh-ansible
```

## Role 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`:

```yaml
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: true
```

### Multi-node configuration

File `inventory/multi-node.yml`:

```yaml
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: true
```

## Single-node playbook

File `playbooks/wazuh-single-node.yml`:

```yaml
- 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:

```bash
ansible-playbook -i inventory/single-node.yml playbooks/wazuh-single-node.yml
```

## Multi-node playbook

File `playbooks/wazuh-multi-node.yml`:

```yaml
- 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:

```bash
ansible-playbook -i inventory/multi-node.yml playbooks/wazuh-multi-node.yml
```

## Variables 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:

```yaml
- 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:

```yaml
- 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

```bash
ansible wazuh_manager -i inventory/multi-node.yml -m shell \
  -a "/var/ossec/bin/agent_control -l"
```

## Custom configuration

### Deploying custom rules

```yaml
- 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: restarted
```

### Configuring integrations

```yaml
- 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: json
```

## Troubleshooting

### SSH connection failures

**Symptoms:** Ansible cannot connect to the target host.

**Solution:**

1. Test SSH access manually:

```bash
ssh deploy@192.168.1.10
```

2. Verify that the SSH key is present in `authorized_keys` on the target host

3. Check the `ansible.cfg` configuration:

```ini
[defaults]
host_key_checking = False
timeout = 30
```

### Package installation failures

**Symptoms:** the role fails during Wazuh package installation.

**Solution:**

1. Verify Wazuh repository availability from the target host:

```bash
curl -s https://packages.wazuh.com/4.x/apt/ | head -5
```

2. Confirm that the repository GPG key is installed

3. For air-gapped networks, configure an [offline repository](/docs/wazuh/deployment/wazuh-offline-installation/)

### Indexer cluster fails to form

**Symptoms:** indexer nodes start but do not join the cluster.

**Solution:**

1. Verify that `indexer_discovery_seed_hosts` contains all node IP addresses

2. Confirm that port 9300 is open between nodes

3. Ensure `indexer_cluster_initial_master_nodes` lists all node names

### Agent fails to register

**Symptoms:** the agent is installed but does not appear in the agent list.

**Solution:**

1. Test manager reachability from the agent host:

```bash
telnet 192.168.1.20 1515
```

2. If password-based enrollment is enabled, verify that passwords match on the manager and agent

3. Check agent logs:

```bash
tail -50 /var/ossec/logs/ossec.log
```

## Additional resources

- [Deployment options overview](/docs/wazuh/deployment/)
- [Wazuh installation guide](/docs/wazuh/installation/)
- [Wazuh Puppet deployment](/docs/wazuh/deployment/wazuh-puppet-deployment/)
- [Official wazuh-ansible repository](https://github.com/wazuh/wazuh-ansible)

