Skip to content

Ready-made examples

Complete recipes, from zero to working. Each one is meant for you to copy and tune to your case: swap the host names, the service and the credentials, and it is ready. They all assume you know the basics of agents, runbooks and automations; if not, you can still follow along, the steps are self-contained.

Recipe Trigger Type Target
1. Restart a Windows service Alert Ansible (WinRM) Windows
2. Restart a Linux service Alert Python (SSH) Linux
3. Scheduled disk cleanup Schedule Ansible Linux
4. Handle a ticket Ticket Python any

Recipe 1: restart a Windows service (from scratch)

The full case: you have a Windows server where the W3SVC service (IIS) sometimes goes down, and you want monitoring to restart it on its own. We go from the empty container to the automation firing.

Step 1: prepare the inventory on the agent host

The agent will talk to Windows over WinRM. The inventory and credentials live on the agent's machine, never on the platform. Create the folder and file:

mkdir -p /opt/sp1-agent/secrets
cat > /opt/sp1-agent/secrets/inventory.ini <<'EOF'
[windows]
win01 ansible_host=10.0.0.21

[windows:vars]
ansible_connection=winrm
ansible_user=Administrator
ansible_password=CHANGE_ME
ansible_winrm_transport=ntlm
ansible_port=5985
ansible_winrm_server_cert_validation=ignore
EOF

The password stays here

ansible_password stays in this file, on the agent's machine. The platform never receives it. In production, prefer a vault (Ansible Vault) or a dedicated service account with the least privilege.

Step 2: bring up the agent with the folder mounted

Generate the agent under Administration → Agents & Identities → Add agent (the token is shown once, copy it). Then start the container mounting the inventory folder into the work directory:

docker run -d --name sp1-agent \
  -e SP1_URL=https://platform.specialone.io \
  -e SP1_AGENT_ID=win-agent \
  -e SP1_AGENT_SECRET=<token-shown-once> \
  -e SP1_TENANT=<tenant-code> \
  -v /opt/sp1-agent/secrets:/opt/sp1-agent/work \
  registry.specialone.io/sp1-automation-agent:latest

The -v is what wires the folder: the inventory.ini you created becomes /opt/sp1-agent/work/inventory.ini inside the agent, and it finds it on its own. In seconds the agent shows up as Active in the list.

Step 3: create the runbook

Under DevOps → Runbooks → Add runbook, type Ansible, name restart-windows-service:

---
- hosts: windows
  gather_facts: false
  tasks:
    - name: Restart the Windows service
      ansible.windows.win_service:
        name: "{{ service | default('W3SVC') }}"
        state: restarted
      register: r

    - name: Confirm it is up
      ansible.windows.win_service_info:
        name: "{{ service | default('W3SVC') }}"
      register: check

    - name: Fail if not running
      ansible.builtin.assert:
        that: check.services[0].state == 'started'
        fail_msg: "Service did not come back"
        success_msg: "Service running after restart"

Notice the playbook already validates: it restarts, reads the state and fails if it did not come back. That way the execution only shows as success when the service is truly up.

Step 4: build the automation

Under DevOps → Automations → Add → Observability:

  • Action: agent win-agent, runbook restart-windows-service.
  • Trigger: on alert. Host win01, alert name *IIS* or *W3SVC*, Critical severity.
  • To target only the host that alerted, add the limit parameter mapping the alert's hostname field (so --limit restricts the run).

Step 5: validate

Before waiting for a real alert, use the Run now tab with service=W3SVC and watch the log live. The PLAY RECAP with failed=0 and the message "Service running after restart" confirm it. From then on, any alert matching the trigger restarts IIS on its own.


Recipe 2: restart a Linux service (on alert)

Same idea as Recipe 1, but on Linux and with Python + SSH, which is handy when you would rather not keep an Ansible inventory and prefer a direct script.

Step 1: SSH key on the agent

On the agent's machine, keep a key that reaches the target hosts (a user with sudo on the service). Mount the folder into the container like in Recipe 1 (-v /opt/sp1-agent/secrets:/opt/sp1-agent/work), with the key at /opt/sp1-agent/secrets/id_ed25519.

Step 2: Python runbook

DevOps → Runbooks → Add, type Python, name restart-service:

import os
import json
import subprocess

params = json.loads(os.environ.get("SP1_PARAMS") or "{}")
host = params.get("hostname") or params.get("host")
service = params.get("service", "nginx")

# validate the input: never trust raw alert data
if not host or not all(c.isalnum() or c in ".-" for c in host):
    raise SystemExit(f"invalid host: {host!r}")

ssh = ["ssh", "-i", "/opt/sp1-agent/work/id_ed25519",
       "-o", "StrictHostKeyChecking=accept-new",
       f"deploy@{host}"]

# restart and confirm the service came back (is-active)
subprocess.run(ssh + ["sudo", "systemctl", "restart", service], check=True, timeout=60)
r = subprocess.run(ssh + ["systemctl", "is-active", service],
                   capture_output=True, text=True, timeout=30)
print(f"{service} on {host}: {r.stdout.strip()}")
raise SystemExit(0 if r.stdout.strip() == "active" else 1)

Step 3: automation

Observability, the Linux agent, runbook restart-service, trigger on alert in the web* group. The alert fields (hostname, alert_name, severity, etc.) arrive on their own in SP1_PARAMS, the script reads hostname directly.

Step 4: validate

Run now tab with host=web03, service=nginx. The log should end with nginx on web03: active and the execution as completed.


Recipe 3: scheduled disk cleanup

Not every automation comes from an alert. This one runs every night, with no problem trigger, just the clock.

Step 1: Ansible runbook

DevOps → Runbooks → Add, type Ansible, name disk-cleanup:

---
- hosts: all
  gather_facts: false
  tasks:
    - name: Find temp files older than 7 days
      ansible.builtin.find:
        paths: /tmp
        age: 7d
        recurse: true
      register: old

    - name: Delete
      ansible.builtin.file:
        path: "{{ item.path }}"
        state: absent
      loop: "{{ old.files }}"
      loop_control: { label: "{{ item.path }}" }

    - name: Summary
      ansible.builtin.debug:
        msg: "{{ old.files | length }} file(s) removed"

Step 2: schedule it

Under DevOps → Job Scheduler → Add:

  • Agent: the one for the target environment.
  • Runbook: disk-cleanup.
  • Frequency: Daily, at 03:00, Monday to Friday.

Each run enters the agent's queue at the scheduled time and shows up in Executions. The schedule is also listed in the agent's Triggers, alongside the alerts and tickets that fire it.

Centralizes what would be cron scattered around

Instead of a crontab per machine (that nobody audits), the routine lives in one place, with a history of each run and the log of what was deleted.


Recipe 4: handle a ticket (and reply on the ticket itself)

The most complete one: a user opens a ticket, the agent runs the action and writes the reply back on the ticket, resolving it on its own. No security risk, it is a common operational help-desk action (restart an application on request).

How it works

flowchart LR C[New ticket<br/>Service restart category]:::c A[Ticket automation]:::a AG[Agent]:::ag T[Ticket updated<br/>comment + resolved]:::t C --> A A -->|triggers, passes the number| AG AG -->|acts and replies via API| T classDef c fill:#fee2e2,stroke:#dc2626,color:#7f1d1d classDef a fill:#6400df,stroke:#4a00b3,color:#ffffff classDef ag fill:#f0e6ff,stroke:#6400df,color:#1e293b classDef t fill:#dcfce7,stroke:#16a34a,color:#14532d

The trick is that the agent receives, along with the ticket data, a platform API token (SP1_API_TOKEN, Service Manager scope). With it, the runbook calls the Service Desk API and writes back on the ticket.

Step 1: runbook that acts and replies

DevOps → Runbooks → Add, type Python, name handle-restart:

import os
import json
import subprocess
import urllib.request

params = json.loads(os.environ.get("SP1_PARAMS") or "{}")
ticket_id = params.get("ticket_id")       # ticket number (comes from the trigger)
host = params.get("host", "app02")
service = params.get("service", "app")

# 1) run the requested action
ok = subprocess.run(
    ["ssh", "-i", "/opt/sp1-agent/work/id_ed25519", f"deploy@{host}",
     "sudo", "systemctl", "restart", service],
    timeout=60,
).returncode == 0

# 2) reply ON THE TICKET ITSELF via the platform API
api = os.environ["SP1_URL"] + f"/api/v2/itsm/tickets/{ticket_id}"
body = {
    "public_log": (f"Service {service} restarted successfully on {host}."
                   if ok else f"Failed to restart {service} on {host}."),
    "status": "resolved" if ok else "assigned",
}
req = urllib.request.Request(
    api, data=json.dumps(body).encode(), method="PATCH",
    headers={"Authorization": "Bearer " + os.environ["SP1_API_TOKEN"],
             "Content-Type": "application/json"},
)
urllib.request.urlopen(req, timeout=30)
print("ticket", ticket_id, "updated")
raise SystemExit(0 if ok else 1)

The public_log becomes a public comment on the ticket (the requester sees it), and status: resolved closes the request. If it fails, it leaves the ticket assigned for a human to look at, instead of resolving it wrong.

Step 2: give the agent the right scope

The agent needs to be able to write to the Service Desk. In Agents & Identities, on the agent's menu → Edit permissions, set Service Manager: Read + write. Without it, the API call is refused.

Step 3: ticket automation

Under DevOps → Automations → Add → Service Management → Ticket triggers agent:

  • Scope: type Request, category "Service restart" (the category/subcategory you use).
  • Agent and runbook: the agent and handle-restart.
  • Parameter map: map the ticket number to the ticket_id parameter (that is what the runbook uses to reply back).

Step 4: validate (safely)

Turn the automation on in dry-run mode first: it shows which tickets would match without running anything. Check the scope, then turn dry-run off. Open a test ticket in the category and watch: in seconds the agent replies on the ticket with the comment and the resolved status, and the execution shows up in Executions.

Pick risk-free actions

Prefer automating operational, reversible actions (restart a service, drain a queue, reload a cache, run a diagnostic and attach the result). Keep out of the automatic flow anything sensitive or irreversible (touching permissions, deleting data, changing passwords), that kind of request should still go through a human.

Next steps

  • Agent details


    Inventory, WinRM, lifecycle and agent security.

    Automation agents

  • Automation details


    The tabs, the schedules, the executions and the report.

    Automations