Skip to content

Automation Recipes

Solve infrastructure problems with these automation scripts.

Basic SSH Operations

Common tasks for connecting to servers and executing commands.

Connect and Execute

from spindlex import SSHClient
from spindlex.hostkeys.policy import RejectPolicy

with SSHClient() as client:
    # Secure default: reject unknown host keys. Make sure the server's key
    # is in ~/.ssh/known_hosts before running (e.g. via `ssh user@host` once).
    client.set_missing_host_key_policy(RejectPolicy())
    client.get_host_keys().load()

    client.connect('server.example.com', username='admin', password='password')

    # exec_command returns (stdin, stdout, stderr)
    stdin, stdout, stderr = client.exec_command('ls -l /tmp')

    # stdout and stderr are iterable and return lines
    for line in stdout:
        print(line.strip())

    # Get the exit status (0 usually means success)
    exit_status = stdout.recv_exit_status()
    print(f"Command exited with status: {exit_status}")

File Transfer (SFTP)

from spindlex import SSHClient

with SSHClient() as client:
    client.connect('server.example.com', username='admin')

    with client.open_sftp() as sftp:
        # Upload a file
        sftp.put('local_file.txt', '/home/admin/remote_file.txt')

        # Download a file
        sftp.get('/var/log/syslog', 'local_syslog.log')

        # List directory
        print("Files in home:")
        for filename in sftp.listdir('/home/admin'):
            print(filename)

Sudo Command Execution

Automate sudo commands by providing the password when prompted.

from spindlex import SSHClient

def run_sudo(client, command, password):
    """
    Executes a command with sudo, handling the password prompt.
    """
    stdin, stdout, stderr = client.exec_command(f"sudo -S {command}")

    # Provide password when sudo asks
    stdin.write(f"{password}\n")

    # Read the response
    return stdout.read().decode()

with SSHClient() as client:
    client.connect('server01', username='admin')
    result = run_sudo(client, "apt-get update", "my-secret-pass")
    print(result)

Parallel Command Execution

Run commands on multiple servers concurrently using SpindleX's native async support.

import asyncio
from spindlex import AsyncSSHClient

async def run_on_server(hostname, command):
    try:
        async with AsyncSSHClient() as client:
            await client.connect(hostname, username='admin')
            stdin, stdout, stderr = await client.exec_command(command)

            # AsyncChannelFile is also async iterable
            async for line in stdout:
                print(f"[{hostname}] {line.strip()}")

            status = await stdout.recv_exit_status()
            print(f"[{hostname}] Finished with status {status}")
    except Exception as e:
        print(f"[{hostname}] Error: {e}")

async def main():
    servers = ['srv1', 'srv2', 'srv3', 'srv4']
    tasks = [run_on_server(s, 'uptime') for s in servers]
    await asyncio.gather(*tasks)

if __name__ == "__main__":
    asyncio.run(main())

SSH ProxyJump (Bastion Hosts)

Connect to a target host through a bastion (jump) host.

from spindlex import SSHClient

with SSHClient() as bastion:
    bastion.connect('bastion.example.com', username='gatekeeper')

    # Open a direct channel to the internal target through the bastion
    transport = bastion.get_transport()
    dest_addr = ('internal-target.lan', 22)

    # Create a direct-tcpip channel (tunnels TCP to target)
    channel = transport.open_channel("direct-tcpip", dest_addr)

    # Connect to target using the channel as a socket
    with SSHClient() as target:
        target.connect(
            'internal-target.lan', 
            username='admin', 
            sock=channel
        )
        stdin, stdout, stderr = target.exec_command('hostname')
        print(f"Connected to: {stdout.read().decode().strip()}")

Real-time Log Tailing

Stream remote log files to your local console in real-time.

from spindlex import SSHClient

with SSHClient() as client:
    client.connect('prod-app-01', username='devops')

    stdin, stdout, stderr = client.exec_command('tail -f /var/log/nginx/access.log')

    print("--- Streaming Remote Logs (Ctrl+C to stop) ---")
    try:
        # ChannelFile is iterable line-by-line
        for line in stdout:
            print(line.strip())
    except KeyboardInterrupt:
        print("Stopping log stream...")

Custom Rekeying Policy

For high-security or high-compliance environments, you can tighten the rekeying thresholds.

from spindlex import SSHClient

with SSHClient() as client:
    client.connect('secure-host', username='audit-user')

    # Rekey every 100MB or every 15 minutes
    transport = client.get_transport()
    transport.set_rekey_policy(
        bytes_limit=100 * 1024 * 1024, 
        time_limit=900
    )

    # Continue with secure operations
    # ...

Keyboard-Interactive Authentication

Use a custom handler to respond to server-driven authentication challenges.

import getpass
from spindlex import SSHClient

def interactive_handler(title, instructions, prompts):
    """
    Handles keyboard-interactive challenges.
    'prompts' is a list of (prompt_text, echo_boolean) tuples.
    """
    answers = []
    print(f"\n--- {title} ---")
    if instructions:
        print(instructions)

    for text, echo in prompts:
        if echo:
            ans = input(text)
        else:
            ans = getpass.getpass(text)
        answers.append(ans)
    return answers

with SSHClient() as client:
    # This will trigger the interactive_handler if the server requests it
    client.connect(
        'mfa-enabled-host', 
        username='user', 
        handler=interactive_handler
    )

GSSAPI/Kerberos Authentication

Authenticate using Kerberos tickets (SSO) in enterprise environments.

from spindlex import SSHClient

with SSHClient() as client:
    # Set gss_auth=True to attempt Kerberos authentication
    client.connect(
        'kerberos-host.internal', 
        username='jdoe',
        gss_auth=True,
        gss_deleg_creds=True
    )
    print("Authenticated via Kerberos!")

SSH Key Rotation

Automate the rotation of public keys across multiple servers.

from spindlex import SSHClient
import os

def rotate_key(client, new_key_path):
    with open(new_key_path, 'r') as f:
        new_key = f.read().strip()

    # Append new key to authorized_keys
    client.exec_command(f'echo "{new_key}" >> ~/.ssh/authorized_keys')
    print("New key added.")

# Example usage
# ...

Backing up Network Configs

Example of a script that backs up a remote configuration file with timestamping.

from spindlex import SSHClient
from datetime import datetime

def backup_config(hostname, remote_path, local_dir):
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    local_path = f"{local_dir}/{hostname}_{timestamp}.conf"

    with SSHClient() as client:
        client.connect(hostname, username='admin')
        with client.open_sftp() as sftp:
            sftp.get(remote_path, local_path)
            print(f"Backup saved to {local_path}")

# backup_config('router01', '/etc/config', './backups')