Guide

Automating Reporting WorkflowsDeep dive

Run a Python Excel Script on Windows Task Scheduler

Register a Python Excel report with Windows Task Scheduler: a self-contained generator, schtasks /Create triggers, the venv interpreter, a .bat wrapper, and logging.

Windows has no cron, so the unattended scheduling you'd do on Linux happens through Task Scheduler instead. This page registers a Python report script to run on a fixed schedule using schtasks, the command-line interface to Task Scheduler, so you can version the command and recreate the task on any machine. It is the Windows companion to Scheduling Python Excel Scripts with Cron; the report logic is identical, only the trigger mechanism changes. By the end you'll have a task that runs whether or not anyone is logged on, writes daily_summary.xlsx, and logs every run.

Prerequisites

  • Windows 10/11 or Windows Server with administrator access (needed to run a task while logged off).
  • Python installed with a virtualenv. Create one and install the report dependencies:
Bat
python -m venv C:\reporting\venv
C:\reporting\venv\Scripts\pip install pandas openpyxl

Note the absolute path C:\reporting\venv\Scripts\python.exe. Task Scheduler does not activate virtualenvs, so you call that interpreter directly — exactly as you'd call the venv Python by absolute path under cron.

A self-contained report script

Save this as C:\reporting\generate_daily_report.py. It uses absolute paths, seeds sample data if none exists, writes daily_summary.xlsx, and exits non-zero on failure so the task's Last Run Result reflects an error:

Python
"""Scheduled Excel report: build data, summarize, write daily_summary.xlsx."""
import sys
import logging
from datetime import datetime
from pathlib import Path

import pandas as pd

# Absolute paths: Task Scheduler's working directory is C:\Windows\System32.
BASE_DIR = Path(r"C:\reporting")
OUTPUT_DIR = BASE_DIR / "output"
LOG_DIR = BASE_DIR / "logs"
for d in (OUTPUT_DIR, LOG_DIR):
    d.mkdir(parents=True, exist_ok=True)

logging.basicConfig(
    filename=LOG_DIR / f"report_{datetime.now():%Y%m%d}.log",
    level=logging.INFO,
    format="%(asctime)s | %(levelname)s | %(message)s",
)

def main():
    logging.info("Starting daily Excel report.")
    try:
        # Build sample data so the run is self-contained.
        df = pd.DataFrame({
            "region": ["North", "South", "North", "East", "South"],
            "revenue": [1200.0, 980.5, 1450.0, 610.25, 980.5],
        })
        summary = df.groupby("region", as_index=False)["revenue"].sum()

        out = OUTPUT_DIR / "daily_summary.xlsx"
        summary.to_excel(out, index=False, engine="openpyxl")
        logging.info("Wrote %d rows to %s", len(summary), out)
    except Exception:
        logging.exception("Report generation failed.")
        sys.exit(1)
    logging.info("Done.")

if __name__ == "__main__":
    main()

Confirm it runs from a normal prompt first:

Bat
C:\reporting\venv\Scripts\python.exe C:\reporting\generate_daily_report.py

A .bat wrapper that logs stdout

Task Scheduler can run python.exe directly, but a small .bat wrapper gives you one place to capture stdout and stderr — the output that appears before your in-script logging starts (a missing module, a syntax error). Save this as C:\reporting\run_report.bat:

Bat
@echo off
REM Activate the venv, then run the report, appending all output to a log.
call C:\reporting\venv\Scripts\activate.bat
python C:\reporting\generate_daily_report.py >> C:\reporting\logs\stdout.log 2>&1

The >> ... 2>&1 redirect is the Windows equivalent of cron's >> logfile 2>&1: without it, interpreter-level errors go nowhere because Task Scheduler discards a task's console output.

Register the task with schtasks

Open an elevated Command Prompt (Run as administrator). The ^ is the line-continuation character in batch — keep it on Windows, or put the whole command on one line.

Daily at 06:00:

Bat
schtasks /Create /TN "DailyExcelReport" /SC DAILY /ST 06:00 ^
  /TR "C:\reporting\run_report.bat" /RL HIGHEST /F

Weekdays only:

Bat
schtasks /Create /TN "DailyExcelReport" /SC WEEKLY /D MON,TUE,WED,THU,FRI /ST 07:30 ^
  /TR "C:\reporting\run_report.bat" /RL HIGHEST /F

Every four hours:

Bat
schtasks /Create /TN "HourlyExcelReport" /SC HOURLY /MO 4 ^
  /TR "C:\reporting\run_report.bat" /RL HIGHEST /F

/F overwrites an existing task with the same name, which makes the command safe to rerun when you redeploy.

Run whether or not the user is logged on

By default a task only fires while its user is interactively logged on. To run it regardless — the usual requirement for a server — supply credentials with /RU and /RP, and request the highest privileges with /RL HIGHEST:

Bat
schtasks /Create /TN "DailyExcelReport" /SC DAILY /ST 06:00 ^
  /TR "C:\reporting\run_report.bat" ^
  /RU "DOMAIN\svc_reports" /RP "the-password" /RL HIGHEST /F

Use a dedicated service account with a non-expiring password for /RU. If you omit /RP, Windows prompts for the password interactively.

schtasks /Create field reference

FlagMeaningExample
/TNTask name (must be unique)"DailyExcelReport"
/TRTask to run — the program or .bat"C:\reporting\run_report.bat"
/SCSchedule typeDAILY, WEEKLY, HOURLY, MINUTE, ONLOGON
/STStart time, 24-hour HH:MM06:00
/DDays (with WEEKLY)MON,TUE,WED,THU,FRI
/MOModifier / interval4 (every 4 hours with HOURLY)
/RURun-as user account"DOMAIN\svc_reports"
/RPRun-as password"the-password"
/RLRun levelHIGHEST or LIMITED
/FForce-overwrite an existing task(no value)

The Task Scheduler GUI, briefly

If you prefer the GUI, open Task Scheduler (taskschd.msc) → Create Task. On the Actions tab set Program/script to C:\reporting\run_report.bat. On Triggers add a daily/weekly schedule. On the General tab tick Run whether user is logged on or not and Run with highest privileges. The result is identical to the schtasks command above — the CLI is just reproducible.

Verify the task

List the task and read its scheduling and last result:

Bat
schtasks /Query /TN "DailyExcelReport" /V /FO LIST

The Last Run Result field is what tells you whether the run succeeded:

Last Run ResultMeaning
0x0Success
0x1Incorrect function / the script exited non-zero (your sys.exit(1))
0x2File not found (check the /TR path)
0x41301Task is currently running
267011Task has not yet run

Force an immediate run to test without waiting for the schedule:

Bat
schtasks /Run /TN "DailyExcelReport"

Then confirm C:\reporting\output\daily_summary.xlsx was written and check C:\reporting\logs\stdout.log.

Common pitfalls

SymptomCauseFix
FileNotFoundError for a file you can seeWorking directory is System32, not your script folderUse absolute paths for every read and write
ModuleNotFoundError: pandasTask ran the system Python, not the venvCall C:\reporting\venv\Scripts\python.exe (or activate in the .bat)
Last Run Result 0x2Path in /TR has spaces and isn't quotedWrap the whole /TR value in double quotes
Task "succeeds" but no file appearsOutput and errors discardedRedirect in the .bat with >> logfile 2>&1
Runs only when you're logged inNo /RU / /RP suppliedAdd a service account and /RL HIGHEST

A note on quoting: paths with spaces (C:\Program Files\...) need quotes inside the already-quoted /TR string, which is awkward. Installing your project under a space-free path like C:\reporting avoids the problem entirely.

Frequently asked questions

Do I need the .bat wrapper, or can I point the task straight at python.exe? You can set /TR "C:\reporting\venv\Scripts\python.exe C:\reporting\generate_daily_report.py" directly. The wrapper is worth it because it gives you one tidy place to capture stdout/stderr to a log and to activate the venv if other tooling expects it.

Why does the task run fine manually but fail on schedule? Almost always the run-as context. A manual /Run uses your logged-on session; the scheduled run may use a service account with a different PATH and working directory. Use absolute paths and confirm the /RU account can read the script and write the output folder.

How do I delete or change a task?schtasks /Delete /TN "DailyExcelReport" /F removes it. To change it, rerun /Create with /F to overwrite, or use schtasks /Change to tweak a single setting like /ST.

Can Task Scheduler email the report after it runs? Not directly — its built-in email action is deprecated. Send mail from Python at the end of the job; see Emailing Excel Reports with smtplib.

Conclusion

Scheduling a Python Excel script on Windows comes down to four things: call the venv python.exe by absolute path, use absolute paths inside the script (the working directory is System32), redirect output to a log in a .bat wrapper, and add /RU//RP//RL HIGHEST so the task runs while logged off. Register it with schtasks /Create /F, verify with schtasks /Query /V, and key your monitoring off the Last Run Result code.

Where to go next

This is the Windows track of Scheduling Python Excel Scripts with Cron. If you'd rather schedule inside Python and stay cross-platform, see the sibling guide Schedule Recurring Excel Reports with APScheduler. To strengthen the report the task produces, see Writing DataFrames to Excel with pandas, and to deliver it, Emailing Excel Reports with smtplib.