Migration Runbook · AM Automate · Internal

Moving AM Automate off the office server to Azure

Every task to migrate the suite to Azure, in the order it has to happen — 7 phases, 26 steps, each with an owner and a time estimate — plus a post-migration QC section covering exactly what to re-check. The execution reference for IT / the Microsoft partner.

Target Azure VM · B4ms (4 vCPU / 16 GB) Term 1-yr reserved DB managed PostgreSQL Access Cloudflare + Entra login ~£110/mo
In scope — all six apps move together: PayFlow SalesFlow Fire Doors WebServe Dispatch CaseFlow
Prep (Phases 1–5)
~2–3 weeks
spread work · zero staff disruption
Cutover (Phase 6)
one evening
~2–3 hrs active + day-one watch
Stabilise + retire (Phase 7)
~1 week soak
then decommission old server
Owner key: IT / MS partner Claude Shared · time estimates are effort, not elapsed — most steps have waiting/soak time around them

Four guardrails that hold across the whole timeline

  1. The old server stays fully live and intact from Phase 1 to step 25. The new one is built and tested alongside it; the old box is never touched until the new one is proven.
  2. No double-processing. During testing the new stack runs with every outward action switched off, so it can't post to Blockman/Xero, ingest mail, or send email twice (step 15).
  3. Links & login must learn the new address. The new hostname goes into the Microsoft login registration (step 14) and into the invoice/quote links (step 20), or logins and links break.
  4. PostgreSQL is part of this move, not later. Leaving SQLite behind is what ends the recurring corruption (Phase 3).
The migration also fixes a legacy design flaw

Today every app is reached through PayFlow — it became the de-facto gateway simply because it was the first tool built, never by design. That's why one app stumbling can take the whole suite down. In the new setup a dedicated gateway is the single entry point and Cloudflare Access is the one central login; PayFlow, SalesFlow, Fire Doors, WebServe, Dispatch and CaseFlow all sit behind it as equal peers. No single app is the front door anymore — the login is (steps 7 & 13).

Phase 1

Provision the cloud

≈ half a day

Nothing else can start until the machine, database and vault exist. These four can all be done in one sitting.

  1. 1ITCreate the Azure VM — B4ms (4 vCPU / 16 GB), 1-yr reserved, Ubuntu, UK South, ~128 GB SSD.1–2 hrs
  2. 2ITCreate the managed PostgreSQL database on private networking, so only the VM can reach it.~1 hr
  3. 3ITCreate the Azure Key Vault that will hold the credentials.~30 min
  4. 4ITLock the network down — no public inbound ports, admin access restricted.~1 hr
Phase 2

Build the environment on the VM

≈ 2–3 days

Rebuild the environment the suite runs in today — on Linux, with sturdier plumbing.

  1. 5ClaudeInstall the runtimes and engine — Python 3.11, Node 20, and Playwright/Chromium (the PDF & Blockman-scraping engine) with its Linux libraries.1–2 hrs
  2. 6ClaudeDeploy all six apps — PayFlow, SalesFlow, Fire Doors, WebServe, Dispatch, CaseFlow — plus the website builder and Cloudflare tunnel, re-pathed from C:\AM Apps\… to /opt/am-automate/….1.5–2.5 days
  3. 7ClaudeStand up a dedicated gateway (reverse proxy) as the single entry point, with each app — PayFlow included — sitting behind it as an equal peer. This ends the legacy setup where every app was routed through PayFlow (an accident of it being built first): now if any one app stops, the rest stay up. Plus systemd services (auto-restart) and cron, replacing Windows Task Scheduler.~1.5 days
Phase 3

Data & secrets

≈ 2–3 days

Move all the apps' data onto managed PostgreSQL / the new host and get every credential into the vault — rehearsed, not rushed at cutover.

  1. 8ClaudeBuild the PostgreSQL schema for the three apps whose data moves onto it — PayFlow, Dispatch and CaseFlow (the SQLite databases). The few SQLite-only settings get made conditional.~½ day
  2. 9BothMove all credentials into Key Vault and wire the apps to read from it instead of local files.~1 day
  3. 10BothRotate anything old or previously exposed — regenerate at source (Xero/Stripe/FixFlo/Microsoft/Blockman), then update the vault.~½ day
  4. 11ClaudeRehearsal data migration — copy the three databases (PayFlow, Dispatch, CaseFlow) into PostgreSQL preserving IDs, and move the file-based stores the other three apps use (SalesFlow documents, Fire Doors cases/quotes, WebServe blog/team). Proves the whole migration works long before cutover.~1 day
Phase 4

Ingress & login

≈ half a day

Stand up the secure public address and the Microsoft-login gate in front of it.

  1. 12ClaudeAdd the new address amautomate.amsbm.co.uk and a named Cloudflare Tunnel from the VM (outbound-only, no open ports).1–2 hrs
  2. 13ClaudeTurn on Cloudflare Access as AM Automate's single central login — one Microsoft (Entra) sign-in, at the front door, that covers every app equally. No app is the gateway anymore; the login is.~1 hr
  3. 14ITAdd the new address to the Microsoft login app registration — without this, logins fail.~30 min
Gate 1Login is now testable. Steps 12–14 must all be done before anyone can sign in to the new stack.
Phase 5

Parallel testing — old server stays live

≈ 3–4 days + soak

Prove the new setup end-to-end while the office server keeps running the business. The QC section at the foot of this document is the detailed checklist for this phase.

  1. 15ClaudeDisarm every outward action on the new stack — mailbox polling, Blockman push, Xero invoicing, email and all timed jobs OFF. This is the guard against double-processing.~½ day
  2. 16ClaudeValidate all six apps end-to-end, read/render only, working through the post-migration QC checklist below — one login reaches every app; dashboards from PostgreSQL; build a quote; render each document type; reach Blockman read-only; the website-deploy path from the new host. Confirm each app stands up independently. Then let it soak a few days.2–3 days
Gate 2Sign-off. Nothing goes live until the QC pass below is clean.
Phase 6

Cutover — one controlled window

≈ 3–5 hrs + day-one watch

The only phase that touches the live business. Everything here happens inside one planned quiet window.

  1. 17BothPick the window (evening/weekend) and tell staff the new address is coming.planning
  2. 18ClaudeFreeze the old server's processing.~15 min
  3. 19ClaudeFinal data sync — copy the delta since the rehearsal and set the mailbox position, so there's no gap and no re-ingest storm.~30–60 min
  4. 20ClaudeRepoint the embedded links — invoice/quote URLs in Blockman uploads and emails — to the new host.~30 min
  5. 21ClaudeHand over the baton — old server's jobs OFF, new stack's jobs ON. Exactly one system is now live.~30 min
  6. 22ClaudeRe-point the control machine — set the IT-cupboard Claude machine to reach the Azure VM (edit the app folder + restart services) instead of the old server, and confirm with a live test edit that goes live. Preserves the single-Claude "talk = live edit" model (master plan §8.1); a hard decommission gate — without it, AM Automate becomes un-editable once the server is off.~1–2 hrs
  7. 23BothSwitch staff to amautomate.amsbm.co.uk — send the address round, update bookmarks.minimal
  8. 24ClaudeWatch day one closely — ingestion, Blockman pushes, no duplicates.~1 day
Phase 7

Stabilise, then retire

≈ 1 week, then ~1 hr

Let the new host earn trust in production before the old one is switched off for good.

  1. 25BothRun clean for several days, with the rollback (re-arm the old server) available the whole time.3–5 days
  2. 26BothOnly once it's proven stable and all four decommission gates are closed (AM Automate soaked, photos verified off, devices re-homed, control machine re-pointed) — decommission the old server.~1 hr
Post-migration QC

What to check before you trust it

The migration moves the environment, not the business logic — the rules, bug-fixes and QC hardening built into the apps travel with the code untouched, so this is a targeted regression pass, not a rebuild. Because the old server stays live throughout, use diff-based QC: run the same real input through both the old and new stacks and compare the output. The areas below are where environmental change realistically shows up, ranked by exposure — work the register first, then the per-app checks.

Regression risk register — ranked by exposure
AreaExposureWhy it's exposedCheck thisFix
Document rendering (PDF fonts) Medium Chromium substitutes any Windows font that isn't on Linux — output can come out with subtly wrong spacing or a different typeface. Eyeball every document type against old output: quote cover, advice letter, breach notice + demand pack, Certificate of Compliance, booking confirmation, PayFlow / CaseFlow letters. Install the matching fonts on the VM.
Database layer
(PayFlow · Dispatch · CaseFlow)
Medium SQLite is loosely typed; PostgreSQL is strict. Risk sits in any raw SQL and in dates/numbers/booleans that were stored loosely. Row counts match the old DB; same invoice in → same routing + GL coding out; same ledger → same eligibility decision. Port the raw SQL; add explicit type casts.
File paths & case-sensitivity
(all six apps)
Low · loud Linux uses / and is case-sensitive; a Windows C:\ path or a wrong-case filename breaks — but it fails immediately and visibly, not silently. Boot each app and watch the logs for file-not-found; grep the code for C:\ and backslashes. Correct the path / filename case.
Gateway & central login
(new architecture)
Low The one deliberate structural change — routing and auth now sit at the gateway instead of running through PayFlow. Each app loads and authenticates behind the new proxy; stopping one app does not take the others down. Adjust the proxy route config.
Scheduled jobs
(rebuilt on cron / systemd)
Low–Med The timing glue is re-implemented from Windows Task Scheduler, so a job could silently fail to fire. After the first scheduled run, confirm each one fired: Blockman sync, GL / nominal-code sync, Stripe sync, booking + funnel emails, quote publish, DB backup. Fix the cron entry / systemd timer.
Cloud integrations
(Blockman · Xero · Stripe · FixFlo · Graph)
Very low Same code, same credentials, same endpoints — reached identically from Linux. Environment-independent. One live read per integration; confirm the Xero token still refreshes from one process only (single-owner). Usually none.
Per-app checks — run during Phase 5 (disarmed)

PayFlow

  • Dashboard tiles all populate from PostgreSQL
  • Open an invoice; GL code picker is populated
  • Dry-run a Blockman push — reaches Blockman, commits nothing

SalesFlow

  • Open a sent document
  • Download the PDF for a contract, a quote and a lease
  • Confirm none render blank

Fire Doors

  • Open a case; build a quote
  • Render the quote PDF, a breach enforcement pack and a Certificate of Compliance
  • Check the funnel email templates render (send disabled)

WebServe

  • Sign in; open the blog and team editors
  • Run a website publish
  • Confirm it deploys from the new host's path

Dispatch

  • Load the app
  • Open a job / record
  • Confirm its data reads from PostgreSQL

CaseFlow

  • Open a case ledger
  • Confirm an eligibility decision matches the old system
  • Confirm the AI review layer runs
The safety net. The old server stays live as your reference the whole time (steps 1–24), so every check above is a comparison against a known-good output, and rollback is always one move away. Budget roughly a week for this pass — materially less than a fresh QC cycle, because nothing about the logic is changing, only the ground it runs on. The one item worth being fussy about is document rendering: it can look fine in a quick glance and still be subtly wrong on a real customer document, so compare those side by side.
The rule running through all 26 steps: the old server stays fully intact and runnable from step 1 to step 25. The new one is built, tested, and even runs alongside the old — but disarmed — and the old box is only switched off at step 26, after the new one has proven itself in production.