A 20-year legacy card system replaced in one day, without opening a firewall or pausing issuance
The card office that issues ID cards for a New Zealand tertiary institution ran on desktop software backed by an on-premises SQL Server database. There was no API, no appetite for inbound firewall rules, and no week in the year when card issuing could simply stop. In August 2026 it moved to CardOffice: rehearsed on a restored copy in the morning, live on the real database by evening, with every one of 12,703 source rows accounted for.
A system that worked, and could not simply be switched off
The institution's card programme ran on desktop software in the campus card office, backed by an on-premises database holding 12,703 rows of cardholder history: students, staff, contractors and access-card holders, most with a photograph, going back to 2002.
The institution uploaded its student roll into that database, the card office issued physical cards from it, and campus IT read card data back out. Every one of those steps had worked for years, and the card office depended on it every working day.
That is the ordinary situation, and it is what makes card migrations hard. Nothing was broken enough to justify an outage, and everything was old enough to make a modern integration awkward.
At a glance
- Customer
- The card office that issues a New Zealand tertiary institution's ID cards
- Scale
- 12,703 source rows: students, staff, contractors and access-card holders
- Migrated from
- A 2002-era card system on an on-premises SQL Server Express database, with no API
- Cutover
- Sunday 9 August 2026, completed in a single day
- Two-way sync live
- 23 August 2026, zero drift across 9,809 live holders
Three things that ruled out a normal migration
The legacy system had no API. The only way in was the database itself, on a machine behind the institution's firewall, and nobody was going to open inbound holes so an outside platform could reach it. That is the correct answer from an IT team, and any approach that needed a different one was never going to happen.
The card office could not stop issuing. There was no window in which the old system could be frozen, exported and switched off. Cards had to keep going out of the door for the whole of the migration.
And the numbers on those cards reach a door. The database allocates the access-control numbers that the campus door system trusts, so a migration that invented, duplicated or reordered one of them would not be a data-quality problem. It would be two people who can open the same door.
Non-negotiable
- No API
- The database was the only interface available
- No inbound access
- No firewall rule, no exposed server, no shared credential
- No downtime
- The card office kept issuing throughout
- No invented numbers
- Door credentials stayed the legacy system's to allocate
An agent that dials out, and a migration rehearsed before it ran
-
The source system was read before anything was moved
Five days were spent reading the database schema, the transform script and the scheduler, so the migration was planned against what the system actually did rather than what it was believed to do. Several findings were resolved before any data moved.
-
Twenty invented people went first
Before a single real record moved, twenty fictional cardholders were run end to end on a restored copy, built deliberately to trip the known hazards: duplicate ID numbers, blank names, an unmapped card type, an over-long surname. Sixteen were created and four were rejected, which was the correct answer. Eight edge cases were fixed while the test set was still fiction.
-
An outbound-only sync agent, on site
A sync agent was installed on the machine that hosts the database. It makes outbound HTTPS calls only. Nothing on the internet can reach into the network, no inbound firewall rule was added, and CardOffice never holds a database credential or learns anything about the campus network. The agent reads through a purpose-built view under a login explicitly denied access to the underlying card table.
-
Rehearsed in the morning, live in the evening
On Sunday 9 August 2026 the migration was run against a restored copy of the production database, checked, and then run on the real database the same day. A verified rollback backup sat on the machine throughout, and a second one on the platform side.
-
Reconciled by exact count
12,500 cardholders created, 203 rows held back, 12,703 rows in the source database. The arithmetic balanced on the day.
-
Two-way sync switched on a fortnight later
Write-back stayed off until the inbound sync had run clean for two weeks. On 23 August 2026 it was enabled, and issue numbers and issue dates for cards printed in CardOffice began flowing back into the legacy database, where the institution's own download reads them exactly as it always has.
The rows that did not import are the reason to trust the ones that did
203 rows were not imported. Around 170 had no name. Around 30 were duplicate ID numbers, two people sharing one identifier, which the import refused to merge on the grounds that guessing which person a card belongs to is not a decision software should make. One surname was too long for the field. One card type had no mapping.
None of these were errors, and none were silently dropped. Each one became a line on a work list for the card office to adjudicate, and a row fixed at the source syncs itself in on the next pass. That is the difference between a migration that reports 98.4% success and one that can tell you the name of every record it did not take.
Checked again after the fact. A reconciliation run after two-way sync went live compared every live cardholder's issue number in both systems and found zero drift, across all 9,809 of them. It is a report that can be run on any schedule, so drift becomes something you get told about rather than something a student discovers at a door.
Two decades of issuance, in one place, for the first time
The legacy desktop software still works. The sync agent keeps both sides current, staff who open the old system see the same data as the new one, and nothing in the migration hinged on a switch-off date. The institution's own upload and download procedures were never asked to change: the interface the institution sees is the same one it has always used, with CardOffice behind it.
Cards, cardholder records, photo capture and approval, issue history and reissues are now managed in CardOffice, in a browser. The only software on site is the set of small agents beside the printer, the encoder and the database.
What we would do differently
The field install taught us things a rehearsal could not. A scheduled task that ran nothing and logged nothing, because of the way Windows handles nested quotes. A database edition that ships with the network protocol the agent needs switched off. A configuration file that exists in two places, so a setting can be changed in the copy nobody is reading.
All three are fixed in the installer now, which is the point of going first. An institution doing this today does not meet them, because this one did.
The pattern is not specific to one database
A system with no API, on a server nobody will expose, that cannot be switched off because people need cards on Monday, is the normal starting position rather than an unusual one. The parts that made this work transfer: read the source system first, rehearse on a restored copy, connect outbound only, reconcile by exact count, and let both systems run current until there is no longer a reason to keep the old one.
Have a legacy system of your own?
Tell us what it is and what it will not let you do. If a migration is the wrong idea we will say so, and if it is the right one you will get the plan before you get a price.