Ansible SNMP Monitoring Network Devices

SNMP (Simple Network Management Protocol) remains the standard for monitoring network devices — switches, routers, firewalls, UPS systems, and servers. Ansible automates SNMP configuration across your entire fleet, ensuring consistent community strings, SNMPv3 credentials, and trap destinations.

Why Automate SNMP with Ansible

  • Consistent security — same SNMPv3 credentials everywhere
  • Rapid deployment — configure hundreds of devices at once
  • Audit trail — version-controlled SNMP configurations
  • Compliance — enforce SNMPv3 (disable v1/v2c) fleet-wide
  • Integration — pair with Prometheus SNMP exporter or Zabbix

SNMPv3 Configuration on Linux Servers

---
- name: Configure SNMPv3 on Linux Servers
  hosts: all
  become: true
  vars:
    snmp_location: "DataCenter-A Rack-12"
    snmp_contact: "ops@example.com"
    snmp_v3_users:
      - username: monitoring
        auth_protocol: SHA-256
        auth_password: "{{ vault_snmp_auth_password }}"
        priv_protocol: AES-256
        priv_password: "{{ vault_snmp_priv_password }}"
        access_level: rouser
    snmp_trap_receivers:
      - host: 10.0.0.50
        port: 162
        version: 3
        username: trap_user
    snmp_disable_v1_v2c: true

  tasks:
    - name: Install SNMP packages
      ansible.builtin.package:
        name:
          - net-snmp
          - net-snmp-utils
        state: present
      when: ansible_os_family == "RedHat"

    - name: Install SNMP packages (Debian)
      ansible.builtin.package:
        name:
          - snmpd
          - snmp
          - libsnmp-dev
        state: present
      when: ansible_os_family == "Debian"

    - name: Stop SNMP before user creation
      ansible.builtin.systemd:
        name: snmpd
        state: stopped

    - name: Deploy SNMP configuration
      ansible.builtin.template:
        src: snmpd.conf.j2
        dest: /etc/snmp/snmpd.conf
        owner: root
        group: root
        mode: "0600"
        backup: true
      notify: Restart SNMPD

    - name: Create SNMPv3 users
      ansible.builtin.command:
        cmd: >
          net-snmp-create-v3-user
          -ro
          -a {{ item.auth_protocol }}
          -A '{{ item.auth_password }}'
          -x {{ item.priv_protocol }}
          -X '{{ item.priv_password }}'
          {{ item.username }}
      loop: "{{ snmp_v3_users }}"
      args:
        creates: /var/lib/net-snmp/snmpd.conf
      no_log: true

    - name: Enable and start SNMPD
      ansible.builtin.systemd:
        name: snmpd
        enabled: true
        state: started

    - name: Verify SNMPv3 connectivity
      ansible.builtin.command:
        cmd: >
          snmpget -v3
          -u {{ snmp_v3_users[0].username }}
          -l authPriv
          -a {{ snmp_v3_users[0].auth_protocol }}
          -A '{{ snmp_v3_users[0].auth_password }}'
          -x {{ snmp_v3_users[0].priv_protocol }}
          -X '{{ snmp_v3_users[0].priv_password }}'
          localhost
          sysUpTime.0
      register: snmp_test
      changed_when: false
      no_log: true
      failed_when: snmp_test.rc != 0

  handlers:
    - name: Restart SNMPD
      ansible.builtin.systemd:
        name: snmpd
        state: restarted

SNMPD Configuration Template

templates/snmpd.conf.j2:

# System information
sysLocation    {{ snmp_location }}
sysContact     {{ snmp_contact }}
sysServices    72

{% if snmp_disable_v1_v2c %}
# Disable SNMPv1 and SNMPv2c (security best practice)
# No rocommunity or rwcommunity directives
{% else %}
# SNMPv2c read-only community (legacy — prefer SNMPv3)
rocommunity {{ snmp_community | default('public') }} {{ snmp_allowed_network | default('127.0.0.1') }}
{% endif %}

# SNMPv3 user access
{% for user in snmp_v3_users %}
{{ user.access_level }} {{ user.username }}
{% endfor %}

# Agent settings
agentAddress udp:161
agentAddress udp6:161

# System view
view systemonly included .1.3.6.1.2.1.1
view systemonly included .1.3.6.1.2.1.25.1

# Full view for authenticated users
view all included .1

# Access control
{% for user in snmp_v3_users %}
{% if user.access_level == 'rouser' %}
rouser {{ user.username }} priv -V all
{% elif user.access_level == 'rwuser' %}
rwuser {{ user.username }} priv -V all
{% endif %}
{% endfor %}

# Trap/inform destinations
{% for receiver in snmp_trap_receivers %}
{% if receiver.version == 3 %}
trapsess -v 3 -u {{ receiver.username }} -l authPriv -a SHA-256 -x AES-256 {{ receiver.host }}:{{ receiver.port }}
{% else %}
trap2sink {{ receiver.host }}:{{ receiver.port }} {{ snmp_community | default('public') }}
{% endif %}
{% endfor %}

# Disk monitoring
disk / 10%
disk /var 10%

# Load monitoring
load 12 10 5

# Process monitoring
{% for proc in snmp_monitored_processes | default([]) %}
proc {{ proc.name }} {{ proc.max | default(0) }} {{ proc.min | default(1) }}
{% endfor %}

# Extend scripts for custom OIDs
{% for extend in snmp_extends | default([]) %}
extend {{ extend.name }} {{ extend.command }}
{% endfor %}

Network Device SNMP Configuration

For Cisco, Arista, and Juniper devices:

- name: Configure SNMP on Cisco IOS devices
  hosts: cisco_switches
  gather_facts: false
  connection: ansible.netcommon.network_cli
  vars:
    snmp_v3_user: monitoring
    snmp_v3_group: MONITOR_GROUP
    snmp_trap_host: 10.0.0.50
  tasks:
    - name: Configure SNMPv3 on Cisco IOS
      cisco.ios.ios_config:
        lines:
          - snmp-server group {{ snmp_v3_group }} v3 priv read FULL_VIEW
          - snmp-server user {{ snmp_v3_user }} {{ snmp_v3_group }} v3 auth sha {{ vault_snmp_auth_password }} priv aes 256 {{ vault_snmp_priv_password }}
          - snmp-server view FULL_VIEW iso included
          - snmp-server location {{ snmp_location }}
          - snmp-server contact {{ snmp_contact }}
          - snmp-server host {{ snmp_trap_host }} version 3 priv {{ snmp_v3_user }}
          - snmp-server enable traps
          - no snmp-server community public
          - no snmp-server community private
      no_log: true

- name: Configure SNMP on Arista EOS
  hosts: arista_switches
  gather_facts: false
  connection: ansible.netcommon.httpapi
  tasks:
    - name: Configure SNMPv3 on Arista
      arista.eos.eos_config:
        lines:
          - snmp-server view FULL_VIEW iso included
          - snmp-server group MONITOR_GROUP v3 priv read FULL_VIEW
          - snmp-server user monitoring MONITOR_GROUP v3 auth sha {{ vault_snmp_auth_password }} priv aes {{ vault_snmp_priv_password }}
          - snmp-server host {{ snmp_trap_host }} version 3 priv monitoring
          - no snmp-server community public
      no_log: true

Prometheus SNMP Exporter Integration

- name: Deploy SNMP Exporter for Prometheus
  hosts: monitoring_servers
  become: true
  vars:
    snmp_exporter_version: "0.26.0"
    snmp_targets:
      - name: core-switch-01
        address: 10.0.0.1
        module: if_mib
      - name: core-router-01
        address: 10.0.0.2
        module: cisco
      - name: ups-01
        address: 10.0.0.100
        module: apcups

  tasks:
    - name: Download SNMP Exporter
      ansible.builtin.get_url:
        url: "https://github.com/prometheus/snmp_exporter/releases/download/v{{ snmp_exporter_version }}/snmp_exporter-{{ snmp_exporter_version }}.linux-amd64.tar.gz"
        dest: /tmp/snmp_exporter.tar.gz

    - name: Extract SNMP Exporter
      ansible.builtin.unarchive:
        src: /tmp/snmp_exporter.tar.gz
        dest: /usr/local/bin/
        remote_src: true
        extra_opts: ["--strip-components=1"]

    - name: Deploy SNMP Exporter configuration
      ansible.builtin.template:
        src: snmp_exporter.yml.j2
        dest: /etc/snmp_exporter/snmp.yml
        mode: "0640"
      notify: Restart SNMP Exporter

    - name: Deploy systemd service
      ansible.builtin.copy:
        content: |
          [Unit]
          Description=Prometheus SNMP Exporter
          After=network.target

          [Service]
          Type=simple
          ExecStart=/usr/local/bin/snmp_exporter --config.file=/etc/snmp_exporter/snmp.yml
          Restart=always
          RestartSec=5

          [Install]
          WantedBy=multi-user.target
        dest: /etc/systemd/system/snmp_exporter.service
        mode: "0644"
      notify: Restart SNMP Exporter

    - name: Start SNMP Exporter
      ansible.builtin.systemd:
        name: snmp_exporter
        enabled: true
        state: started
        daemon_reload: true

    - name: Add Prometheus scrape config for SNMP targets
      ansible.builtin.blockinfile:
        path: /etc/prometheus/prometheus.yml
        insertafter: "scrape_configs:"
        block: |
          - job_name: 'snmp'
            scrape_interval: 60s
            scrape_timeout: 30s
            static_configs:
          {% for target in snmp_targets %}
              - targets: ['{{ target.address }}']
                labels:
                  device: '{{ target.name }}'
                  module: '{{ target.module }}'
          {% endfor %}
            metrics_path: /snmp
            params:
              auth: ['monitoring_v3']
            relabel_configs:
              - source_labels: [__address__]
                target_label: __param_target
              - source_labels: [module]
                target_label: __param_module
              - target_label: __address__
                replacement: localhost:9116
      notify: Restart Prometheus

  handlers:
    - name: Restart SNMP Exporter
      ansible.builtin.systemd:
        name: snmp_exporter
        state: restarted

    - name: Restart Prometheus
      ansible.builtin.systemd:
        name: prometheus
        state: reloaded

SNMP Trap Receiver (snmptrapd)

- name: Configure SNMP Trap Receiver
  hosts: monitoring_servers
  become: true
  tasks:
    - name: Install snmptrapd
      ansible.builtin.package:
        name: net-snmp-utils
        state: present

    - name: Deploy snmptrapd configuration
      ansible.builtin.copy:
        content: |
          # SNMPv3 user for receiving traps
          createUser -e 0x8000000001020304 trap_user SHA-256 "{{ vault_snmp_auth_password }}" AES-256 "{{ vault_snmp_priv_password }}"
          authUser log,execute trap_user priv

          # Log traps to file
          [snmptrapd]
          logOption f /var/log/snmptrapd.log

          # Forward traps to trap handler script
          traphandle default /usr/local/bin/snmp_trap_handler.sh
        dest: /etc/snmp/snmptrapd.conf
        owner: root
        group: root
        mode: "0600"
      no_log: true
      notify: Restart snmptrapd

    - name: Deploy trap handler script
      ansible.builtin.copy:
        content: |
          #!/bin/bash
          # Forward SNMP traps to alerting system
          read host
          read ip
          while read oid val; do
            echo "$(date -Iseconds) $host $ip $oid $val" >> /var/log/snmp_traps.log
            # Send to webhook
            curl -s -X POST http://alertmanager:9093/api/v1/alerts \
              -H "Content-Type: application/json" \
              -d "[{\"labels\":{\"alertname\":\"SNMPTrap\",\"device\":\"$host\",\"oid\":\"$oid\"},\"annotations\":{\"value\":\"$val\"}}]"
          done
        dest: /usr/local/bin/snmp_trap_handler.sh
        mode: "0755"

    - name: Enable snmptrapd
      ansible.builtin.systemd:
        name: snmptrapd
        enabled: true
        state: started

  handlers:
    - name: Restart snmptrapd
      ansible.builtin.systemd:
        name: snmptrapd
        state: restarted

Security Hardening

- name: SNMP security audit and hardening
  hosts: all
  become: true
  tasks:
    - name: Ensure no default community strings
      ansible.builtin.lineinfile:
        path: /etc/snmp/snmpd.conf
        regexp: "^(ro|rw)community\\s+(public|private)"
        state: absent
      notify: Restart SNMPD

    - name: Restrict SNMP to management network
      ansible.builtin.iptables:
        chain: INPUT
        protocol: udp
        destination_port: "161"
        source: "{{ snmp_management_network }}"
        jump: ACCEPT
        comment: "Allow SNMP from management network"

    - name: Block SNMP from all other sources
      ansible.builtin.iptables:
        chain: INPUT
        protocol: udp
        destination_port: "161"
        jump: DROP
        comment: "Block SNMP from unauthorized sources"

    - name: Verify no SNMPv1/v2c access
      ansible.builtin.command:
        cmd: snmpget -v2c -c public localhost sysUpTime.0
      register: v2c_test
      changed_when: false
      failed_when: v2c_test.rc == 0
      ignore_errors: true

Troubleshooting

ProblemCauseSolution
"Timeout: No Response"Firewall or wrong communityCheck UDP 161, verify credentials
"Authentication failure"Wrong auth/priv passwordsRecreate SNMPv3 user
"No Such Object"OID not in viewExpand SNMP view to include OID tree
Incomplete walk resultsAgent maxGetbulk too lowIncrease maxGetbulkRepeats
High CPU from SNMP pollingToo frequent pollingIncrease scrape_interval to 60s+

Conclusion

SNMP monitoring with Ansible gives you consistent, secure, auditable device monitoring across your entire infrastructure. By enforcing SNMPv3, disabling legacy community strings, and integrating with modern monitoring stacks like Prometheus, you get the broad device coverage of SNMP with the security and observability standards of a modern platform.