Systems Library / Operations & Admin / How to Build an Employee Offboarding Automation System
Operations & Admin hr people

How to Build an Employee Offboarding Automation System

Automate account deactivation, asset recovery, and exit workflows.

Jay Banlasan

Jay Banlasan

The AI Systems Guy

When someone leaves your company, forgetting to revoke access is a security risk. I built a system to automate the employee offboarding process so that account deactivation, asset recovery, and knowledge transfer happen in the right order every time.

This system reverses the provisioning pipeline and tracks every step to completion.

What You Need Before Starting

Step 1: Define the Offboarding Checklist

OFFBOARDING_STEPS = {
    "immediate": [
        {"task": "Disable email account", "owner": "IT", "security": True},
        {"task": "Revoke VPN access", "owner": "IT", "security": True},
        {"task": "Remove from Slack", "owner": "IT", "security": True},
        {"task": "Revoke GitHub access", "owner": "IT", "security": True},
        {"task": "Disable AWS credentials", "owner": "IT", "security": True}
    ],
    "within_24h": [
        {"task": "Collect company laptop", "owner": "IT", "security": False},
        {"task": "Collect access badges", "owner": "Facilities", "security": False},
        {"task": "Forward email to manager", "owner": "IT", "security": False},
        {"task": "Transfer file ownership", "owner": "IT", "security": False}
    ],
    "within_week": [
        {"task": "Knowledge transfer documentation", "owner": "Manager", "security": False},
        {"task": "Final payroll processing", "owner": "HR", "security": False},
        {"task": "Benefits termination notice", "owner": "HR", "security": False},
        {"task": "Exit interview scheduled", "owner": "HR", "security": False},
        {"task": "Remove from payroll system", "owner": "HR", "security": False}
    ]
}

Step 2: Create the Offboarding Tracker

import sqlite3
from datetime import datetime, timedelta

def init_offboarding_db(db_path="offboarding.db"):
    conn = sqlite3.connect(db_path)
    conn.execute("""
        CREATE TABLE IF NOT EXISTS offboarding (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            employee_name TEXT,
            department TEXT,
            last_day TEXT,
            initiated_at TEXT,
            status TEXT DEFAULT 'in_progress'
        )
    """)
    conn.execute("""
        CREATE TABLE IF NOT EXISTS offboarding_tasks (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            offboarding_id INTEGER,
            task TEXT,
            owner TEXT,
            phase TEXT,
            due_date TEXT,
            security_critical INTEGER,
            completed INTEGER DEFAULT 0,
            completed_at TEXT
        )
    """)
    conn.commit()
    return conn

def start_offboarding(conn, employee_name, department, last_day):
    cursor = conn.execute(
        "INSERT INTO offboarding (employee_name, department, last_day, initiated_at) VALUES (?,?,?,?)",
        (employee_name, department, last_day, datetime.now().isoformat())
    )
    offboarding_id = cursor.lastrowid
    last_day_dt = datetime.strptime(last_day, "%Y-%m-%d")

    for phase, tasks in OFFBOARDING_STEPS.items():
        if phase == "immediate":
            due = last_day
        elif phase == "within_24h":
            due = (last_day_dt + timedelta(days=1)).strftime("%Y-%m-%d")
        else:
            due = (last_day_dt + timedelta(days=7)).strftime("%Y-%m-%d")

        for task in tasks:
            conn.execute(
                "INSERT INTO offboarding_tasks (offboarding_id, task, owner, phase, due_date, security_critical) VALUES (?,?,?,?,?,?)",
                (offboarding_id, task["task"], task["owner"], phase, due, int(task["security"]))
            )

    conn.commit()
    return offboarding_id

Step 3: Execute Security-Critical Steps First

def execute_security_steps(conn, offboarding_id):
    tasks = conn.execute("""
        SELECT id, task FROM offboarding_tasks
        WHERE offboarding_id=? AND security_critical=1 AND completed=0
    """, (offboarding_id,)).fetchall()

    for task_id, task_name in tasks:
        print(f"Executing: {task_name}")
        # API calls to revoke access would go here
        conn.execute(
            "UPDATE offboarding_tasks SET completed=1, completed_at=? WHERE id=?",
            (datetime.now().isoformat(), task_id)
        )

    conn.commit()
    return len(tasks)

Step 4: Track Progress and Escalate

def offboarding_status(conn, offboarding_id):
    total = conn.execute(
        "SELECT COUNT(*) FROM offboarding_tasks WHERE offboarding_id=?",
        (offboarding_id,)
    ).fetchone()[0]

    completed = conn.execute(
        "SELECT COUNT(*) FROM offboarding_tasks WHERE offboarding_id=? AND completed=1",
        (offboarding_id,)
    ).fetchone()[0]

    overdue = conn.execute(
        "SELECT task, owner, due_date FROM offboarding_tasks WHERE offboarding_id=? AND completed=0 AND due_date < ?",
        (offboarding_id, datetime.now().strftime("%Y-%m-%d"))
    ).fetchall()

    return {
        "progress": f"{completed}/{total}",
        "percent": round(completed/total*100) if total else 0,
        "overdue_tasks": [{"task": t[0], "owner": t[1], "due": t[2]} for t in overdue]
    }

Step 5: Generate the Offboarding Report

def generate_report(conn, offboarding_id):
    info = conn.execute(
        "SELECT employee_name, department, last_day FROM offboarding WHERE id=?",
        (offboarding_id,)
    ).fetchone()

    status = offboarding_status(conn, offboarding_id)

    report = f"# Offboarding Report: {info[0]}\n\n"
    report += f"Department: {info[1]} | Last Day: {info[2]}\n"
    report += f"Progress: {status['progress']} ({status['percent']}%)\n\n"

    if status["overdue_tasks"]:
        report += "## Overdue Tasks\n\n"
        for task in status["overdue_tasks"]:
            report += f"- {task['task']} (assigned to {task['owner']}, due {task['due']})\n"

    return report

What to Build Next

Connect this to your provisioning system so offboarding automatically reverses every access grant from onboarding. Full lifecycle management with one data source.

Related Reading

Want this system built for your business?

Get a free assessment. We will map every system your business needs and show you the ROI.

Get Your Free Assessment

Related Systems