AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

Ansible for SIEM and SOC: Automate Security Operations, Incident Response, and Compliance

By Luca Berton · Published 2024-01-01 · Category: installation

Automate SOC and SIEM operations with Ansible. Incident response playbooks, threat hunting automation, SOAR integration, compliance scanning, log collection.

Why Ansible for Security Operations?

Security Operations Centers (SOCs) face a fundamental scaling problem: alerts grow faster than headcount. The average SOC processes 10,000+ alerts per day while facing a chronic analyst shortage. Automation is not optional — it's survival.

Ansible fits SOC automation because:

  • Agentless — no software to install on endpoints or network devices
  • Human-readable — security analysts can read and write YAML playbooks without being developers
  • Cross-platform — orchestrate firewalls, endpoints, SIEM, ticketing, cloud, and network in a single workflow
  • Auditable — every action logged, every playbook version-controlled
  • Idempotent — safe to re-run during an incident without causing additional damage

SIEM Integration

Collect and Forward Logs

---
- name: Configure centralized logging
  hosts: all
  become: true

  vars:
    siem_server: "siem.company.com"
    siem_port: 514
    siem_protocol: tcp

  tasks:
    - name: Install rsyslog
      ansible.builtin.package:
        name: rsyslog
        state: present

    - name: Configure remote syslog forwarding
      ansible.builtin.template:
        src: rsyslog-forward.conf.j2
        dest: /etc/rsyslog.d/50-siem-forward.conf
        mode: '0644'
      notify: restart rsyslog

    - name: Ensure audit logging enabled
      ansible.builtin.package:
        name: auditd
        state: present

    - name: Deploy audit rules for security monitoring
      ansible.builtin.copy:
        content: |
          # Monitor authentication files
          -w /etc/passwd -p wa -k identity
          -w /etc/shadow -p wa -k identity
          -w /etc/group -p wa -k identity
          -w /etc/gshadow -p wa -k identity

          # Monitor sudo usage
          -w /etc/sudoers -p wa -k sudo_changes
          -w /etc/sudoers.d/ -p wa -k sudo_changes

          # Monitor SSH configuration
          -w /etc/ssh/sshd_config -p wa -k sshd_config

          # Monitor cron
          -w /etc/crontab -p wa -k cron
          -w /etc/cron.d/ -p wa -k cron

          # Monitor login/logout
          -w /var/log/lastlog -p wa -k logins
          -w /var/log/faillog -p wa -k logins

          # Monitor privilege escalation
          -a always,exit -F arch=b64 -S execve -C uid!=euid -k escalation
        dest: /etc/audit/rules.d/security.rules
        mode: '0640'
      notify: restart auditd

  handlers:
    - name: restart rsyslog
      ansible.builtin.systemd:
        name: rsyslog
        state: restarted

    - name: restart auditd
      ansible.builtin.service:
        name: auditd
        state: restarted

Splunk Integration

---
- name: Deploy Splunk Universal Forwarder
  hosts: all
  become: true

  vars:
    splunk_server: "splunk-indexer.company.com"
    splunk_port: 9997
    splunk_forwarder_url: "https://download.splunk.com/products/universalforwarder/releases/9.3.0/linux/splunkforwarder-9.3.0-linux-x86_64.tgz"

  tasks:
    - name: Download Splunk UF
      ansible.builtin.get_url:
        url: "{{ splunk_forwarder_url }}"
        dest: /tmp/splunkforwarder.tgz

    - name: Extract Splunk UF
      ansible.builtin.unarchive:
        src: /tmp/splunkforwarder.tgz
        dest: /opt
        remote_src: true
        creates: /opt/splunkforwarder

    - name: Configure forwarding
      ansible.builtin.template:
        src: splunk-outputs.conf.j2
        dest: /opt/splunkforwarder/etc/system/local/outputs.conf
      notify: restart splunk-forwarder

    - name: Configure inputs
      ansible.builtin.copy:
        content: |
          [monitor:///var/log/auth.log]
          sourcetype = linux_secure
          index = security

          [monitor:///var/log/audit/audit.log]
          sourcetype = linux_audit
          index = security

          [monitor:///var/log/syslog]
          sourcetype = syslog
          index = main
        dest: /opt/splunkforwarder/etc/system/local/inputs.conf
      notify: restart splunk-forwarder

    - name: Start Splunk Forwarder
      ansible.builtin.command:
        cmd: /opt/splunkforwarder/bin/splunk start --accept-license --no-prompt
        creates: /opt/splunkforwarder/var/run/splunk/splunkd.pid

  handlers:
    - name: restart splunk-forwarder
      ansible.builtin.command:
        cmd: /opt/splunkforwarder/bin/splunk restart

Elastic SIEM (ELK)

---
- name: Deploy Elastic Agent for SIEM
  hosts: all
  become: true

  vars:
    fleet_url: "https://fleet.company.com:8220"
    enrollment_token: "{{ vault_elastic_token }}"

  tasks:
    - name: Download Elastic Agent
      ansible.builtin.get_url:
        url: "https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-8.16.0-linux-x86_64.tar.gz"
        dest: /tmp/elastic-agent.tar.gz

    - name: Extract agent
      ansible.builtin.unarchive:
        src: /tmp/elastic-agent.tar.gz
        dest: /opt
        remote_src: true

    - name: Enroll agent with Fleet
      ansible.builtin.command:
        cmd: >
          /opt/elastic-agent-8.16.0-linux-x86_64/elastic-agent install
          --url={{ fleet_url }}
          --enrollment-token={{ enrollment_token }}
          --non-interactive
        creates: /opt/Elastic/Agent/elastic-agent

See also: Ansible for SOC and SIEM: Automate Security Operations Complete Guide

Incident Response Automation

Automated Host Isolation

When a compromise is detected, automatically isolate the host while preserving forensic evidence:

---
- name: Isolate compromised host
  hosts: "{{ compromised_host }}"
  become: true

  vars:
    soc_server: "10.0.0.5"
    siem_server: "10.0.0.10"
    ticket_id: "{{ incident_ticket }}"

  tasks:
    - name: Capture network connections before isolation
      ansible.builtin.shell: |
        ss -tunap > /root/forensics/connections_{{ ansible_date_time.iso8601 }}.txt
        netstat -rn > /root/forensics/routes_{{ ansible_date_time.iso8601 }}.txt
        ip addr show > /root/forensics/interfaces_{{ ansible_date_time.iso8601 }}.txt
      args:
        creates: /root/forensics

    - name: Create forensics directory
      ansible.builtin.file:
        path: /root/forensics
        state: directory
        mode: '0700'

    - name: Capture running processes
      ansible.builtin.shell: |
        ps auxww > /root/forensics/processes.txt
        lsof -i > /root/forensics/open_files.txt
      changed_when: false

    - name: Capture memory-mapped files for suspicious processes
      ansible.builtin.shell: |
        for pid in $(ps aux | awk '/SUSPECT_PROCESS/ {print $2}'); do
          cat /proc/$pid/maps > /root/forensics/maps_${pid}.txt 2>/dev/null
          ls -la /proc/$pid/fd/ > /root/forensics/fd_${pid}.txt 2>/dev/null
        done
      changed_when: false
      ignore_errors: true

    - name: Block all traffic except SOC and SIEM
      community.general.ufw:
        rule: allow
        from_ip: "{{ item }}"
        comment: "Incident {{ ticket_id }} - allowed during isolation"
      loop:
        - "{{ soc_server }}"
        - "{{ siem_server }}"

    - name: Set default deny
      community.general.ufw:
        default: deny
        direction: "{{ item }}"
      loop:
        - incoming
        - outgoing

    - name: Enable firewall
      community.general.ufw:
        state: enabled

    - name: Disable cron to prevent lateral movement
      ansible.builtin.systemd:
        name: cron
        state: stopped
        enabled: false

    - name: Lock all non-root user accounts
      ansible.builtin.shell: |
        for user in $(awk -F: '$3 >= 1000 && $3 != 65534 {print $1}' /etc/passwd); do
          usermod -L "$user"
          echo "Locked: $user" >> /root/forensics/locked_users.txt
        done
      changed_when: true

    - name: Send isolation notification
      ansible.builtin.uri:
        url: "https://hooks.slack.com/services/{{ vault_slack_webhook }}"
        method: POST
        body_format: json
        body:
          text: "🔴 Host {{ inventory_hostname }} isolated for incident {{ ticket_id }}"

Automated Threat Hunting

---
- name: Hunt for indicators of compromise
  hosts: all
  become: true

  vars:
    ioc_hashes:
      - "e99a18c428cb38d5f260853678922e03"
      - "d41d8cd98f00b204e9800998ecf8427e"
    ioc_ips:
      - "185.220.101.0/24"
      - "45.155.205.0/24"
    suspicious_ports:
      - 4444
      - 5555
      - 8888
      - 1337

  tasks:
    - name: Check for known malicious file hashes
      ansible.builtin.shell: |
        find / -type f -executable -newer /etc/hostname -exec md5sum {} \; 2>/dev/null | head -1000
      register: file_hashes
      changed_when: false
      timeout: 120

    - name: Flag IOC hash matches
      ansible.builtin.set_fact:
        ioc_matches: "{{ file_hashes.stdout_lines | select('search', item) | list }}"
      loop: "{{ ioc_hashes }}"
      register: hash_results

    - name: Check for connections to known bad IPs
      ansible.builtin.shell: |
        ss -tunap | grep -E "{{ ioc_ips | join('|') }}" || true
      register: bad_connections
      changed_when: false

    - name: Check for suspicious listening ports
      ansible.builtin.shell: |
        ss -tlnp | grep -E ":{{ suspicious_ports | join('|:') }}" || true
      register: suspicious_listeners
      changed_when: false

    - name: Check for unauthorized SSH keys
      ansible.builtin.find:
        paths: /home
        patterns: "authorized_keys"
        recurse: true
      register: ssh_keys

    - name: Check for suspicious cron jobs
      ansible.builtin.shell: |
        for user in $(cut -d: -f1 /etc/passwd); do
          crontab -l -u "$user" 2>/dev/null | grep -v "^#" | grep -v "^$"
        done
      register: cron_jobs
      changed_when: false

    - name: Check for rootkits (basic)
      ansible.builtin.shell: |
        # Check for hidden processes
        diff <(ps aux | awk '{print $2}' | sort -n) <(ls /proc | grep -E '^[0-9]+$' | sort -n) || true
        # Check for modified system binaries
        rpm -Va 2>/dev/null | grep -E '^..5' || dpkg --verify 2>/dev/null | grep -v "^$" || true
      register: rootkit_check
      changed_when: false

    - name: Generate hunt report
      ansible.builtin.template:
        src: hunt-report.j2
        dest: "/tmp/hunt_{{ inventory_hostname }}_{{ ansible_date_time.date }}.txt"
      delegate_to: localhost

Compliance Automation

CIS Benchmark Scanning

---
- name: CIS benchmark compliance check
  hosts: all
  become: true

  tasks:
    - name: Check SSH configuration
      ansible.builtin.lineinfile:
        path: /etc/ssh/sshd_config
        regexp: "{{ item.regexp }}"
        line: "{{ item.line }}"
        state: present
      loop:
        - { regexp: '^PermitRootLogin', line: 'PermitRootLogin no' }
        - { regexp: '^PasswordAuthentication', line: 'PasswordAuthentication no' }
        - { regexp: '^X11Forwarding', line: 'X11Forwarding no' }
        - { regexp: '^MaxAuthTries', line: 'MaxAuthTries 4' }
        - { regexp: '^ClientAliveInterval', line: 'ClientAliveInterval 300' }
        - { regexp: '^ClientAliveCountMax', line: 'ClientAliveCountMax 0' }
        - { regexp: '^LoginGraceTime', line: 'LoginGraceTime 60' }
        - { regexp: '^Protocol', line: 'Protocol 2' }
      notify: restart sshd

    - name: Ensure password expiration
      ansible.builtin.lineinfile:
        path: /etc/login.defs
        regexp: "{{ item.regexp }}"
        line: "{{ item.line }}"
      loop:
        - { regexp: '^PASS_MAX_DAYS', line: 'PASS_MAX_DAYS 90' }
        - { regexp: '^PASS_MIN_DAYS', line: 'PASS_MIN_DAYS 7' }
        - { regexp: '^PASS_WARN_AGE', line: 'PASS_WARN_AGE 14' }

    - name: Ensure core dumps restricted
      ansible.builtin.lineinfile:
        path: /etc/security/limits.conf
        line: "* hard core 0"
        state: present

    - name: Disable USB storage
      ansible.builtin.copy:
        content: "install usb-storage /bin/true"
        dest: /etc/modprobe.d/usb-storage.conf
        mode: '0644'

    - name: Set file permissions on sensitive files
      ansible.builtin.file:
        path: "{{ item.path }}"
        mode: "{{ item.mode }}"
        owner: root
        group: "{{ item.group | default('root') }}"
      loop:
        - { path: /etc/passwd, mode: '0644' }
        - { path: /etc/shadow, mode: '0640', group: shadow }
        - { path: /etc/group, mode: '0644' }
        - { path: /etc/gshadow, mode: '0640', group: shadow }
        - { path: /etc/crontab, mode: '0600' }

  handlers:
    - name: restart sshd
      ansible.builtin.systemd:
        name: sshd
        state: restarted

Vulnerability Scanning and Remediation

---
- name: Vulnerability management
  hosts: all
  become: true

  tasks:
    - name: Check for available security updates
      ansible.builtin.apt:
        update_cache: true
        upgrade: "no"
      register: apt_updates
      check_mode: true
      when: ansible_os_family == "Debian"

    - name: Get list of security updates
      ansible.builtin.shell: |
        apt list --upgradable 2>/dev/null | grep -i security
      register: security_updates
      changed_when: false
      when: ansible_os_family == "Debian"

    - name: Apply security patches (maintenance window)
      ansible.builtin.apt:
        upgrade: safe
        update_cache: true
      when:
        - ansible_os_family == "Debian"
        - maintenance_window | default(false)
      notify: check reboot required

    - name: Scan for CVEs in installed packages
      ansible.builtin.shell: |
        if command -v grype &>/dev/null; then
          grype dir:/ --only-fixed -o table 2>/dev/null | head -50
        else
          echo "grype not installed"
        fi
      register: cve_scan
      changed_when: false

  handlers:
    - name: check reboot required
      ansible.builtin.stat:
        path: /var/run/reboot-required
      register: reboot_file

    - name: notify reboot needed
      ansible.builtin.debug:
        msg: "⚠️ {{ inventory_hostname }} requires reboot after security patches"
      when: reboot_file.stat.exists | default(false)

See also: Ansible splunk.es Collection: Automating Security Incident Response Workflows

Firewall Orchestration

---
- name: Block threat actor across all firewalls
  hosts: firewalls
  
  vars:
    threat_ips:
      - "185.220.101.42"
      - "45.155.205.19"
    ticket_id: "IR-2026-0142"

  tasks:
    - name: Block on Palo Alto
      paloaltonetworks.panos.panos_address_object:
        provider: "{{ panos_provider }}"
        name: "block-{{ item | replace('.', '-') }}"
        value: "{{ item }}"
        description: "Blocked - {{ ticket_id }}"
      loop: "{{ threat_ips }}"
      when: "'paloalto' in group_names"

    - name: Block on Linux firewalls
      community.general.ufw:
        rule: deny
        from_ip: "{{ item }}"
        comment: "Blocked - {{ ticket_id }}"
      loop: "{{ threat_ips }}"
      when: "'linux' in group_names"

    - name: Block on Cisco ASA
      cisco.asa.asa_acls:
        config:
          - afi: ipv4
            acls:
              - name: OUTSIDE_IN
                aces:
                  - grant: deny
                    source:
                      address: "{{ item }}"
                    destination:
                      any: true
        state: merged
      loop: "{{ threat_ips }}"
      when: "'cisco_asa' in group_names"

Integration with SOAR Platforms

Trigger Ansible from SOAR

Configure AAP webhooks to receive SOAR triggers:

# AAP job template with survey for dynamic parameters
# SOAR calls AAP API to launch jobs:

# curl -X POST "https://aap.company.com/api/controller/v2/job_templates/42/launch/" \
#   -H "Authorization: Bearer $TOKEN" \
#   -H "Content-Type: application/json" \
#   -d '{
#     "extra_vars": {
#       "compromised_host": "web-03.company.com",
#       "incident_ticket": "IR-2026-0142",
#       "action": "isolate"
#     }
#   }'

SOAR-Driven Response Playbook

---
- name: SOAR-triggered incident response
  hosts: "{{ compromised_host }}"
  become: true

  tasks:
    - name: Isolate host
      ansible.builtin.include_role:
        name: incident_response
        tasks_from: isolate.yml
      when: action == "isolate"

    - name: Collect forensics
      ansible.builtin.include_role:
        name: incident_response
        tasks_from: collect_evidence.yml
      when: action == "forensics"

    - name: Block IOCs
      ansible.builtin.include_role:
        name: incident_response
        tasks_from: block_iocs.yml
      when: action == "block"
      delegate_to: "{{ item }}"
      loop: "{{ groups['firewalls'] }}"

    - name: Restore host
      ansible.builtin.include_role:
        name: incident_response
        tasks_from: restore.yml
      when: action == "restore"

See also: Security Best Practices for Ansible Automation Platform 2.6

FAQ

Can Ansible replace a SOAR platform?

Ansible can handle the orchestration and automation parts of SOAR (Security Orchestration, Automation, and Response) but lacks the case management, analyst workflow, and threat intelligence platform features. Use Ansible as the execution engine behind a SOAR platform like Splunk SOAR, Palo Alto XSOAR, or IBM QRadar SOAR.

Is Ansible secure enough for SOC operations?

Yes, with proper configuration. Use AAP for centralized credential management (no passwords in playbooks), RBAC (limit who can run what), audit logging (every job tracked), and encrypted variables (Ansible Vault). AAP integrates with CyberArk, HashiCorp Vault, and other enterprise secret managers.

How does Ansible compare to dedicated security automation tools?

Ansible is general-purpose automation that excels at cross-platform orchestration. Dedicated security tools (CrowdStrike Falcon, SentinelOne) have deeper endpoint visibility. The best approach: use Ansible to orchestrate responses across your full stack while dedicated tools handle detection and endpoint telemetry.

Conclusion

Ansible bridges the gap between security detection and response. Deploy SIEM agents across thousands of endpoints, automate incident response with host isolation playbooks, run compliance scans against CIS benchmarks, orchestrate firewall blocks across multi-vendor environments, and integrate with SOAR platforms — all with human-readable YAML that security analysts can understand and modify. Combined with AAP's RBAC, scheduling, and audit capabilities, Ansible becomes the execution layer for enterprise security operations.

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home