cd ~cases/remote-office.md

case 03LIVE · RUNNING AT THE CLIENT

A remote office for 19 workstations

In two weeks, fully remotely, I deployed a turnkey terminal server for a company. 19 employees work in their accounting software through personal VPN keys, the server is closed to the internet, and a nightly backup runs with a tested restore.

Timeline
October 2026 · ~2 weeks · fully remote
Stack
Windows Server 2022 · Hyper-V · Remote Desktop Services · WireGuard · PowerShell · SQL Server Express · AppLocker
Built by
Forge Logic
19workstations, each with its own VPN key
0ports open to the outside, per external scan
7nightly server copies with automatic integrity checks
2 minto restore from a copy, tested in practice
~2 wksfrom an empty server to a working office
0site visits — everything done remotely
A remote office for 19 workstations0:29

What was broken

The office lost its server and its administrator at the same time. The client had come to me before with an RDP printing issue, so they came back. This time, everything was on fire.

  • The server went down along with the account. A large European hosting provider closed the client’s account after a data check, and the appeal was rejected. The office stopped working together with the server.
  • The previous administrator stopped responding. There was no documentation and no handover.
  • I couldn’t come to the office. Everything, from the first login to printing receipts, had to be done remotely.

Client requirements

  • Thin clients in the office; everyone else connects from home over VPN.
  • Everyone works in one accounting system backed by an SQL database.
  • A4 invoices and receipts printed in another office, on employees’ own printers.
  • “So a virus can’t wipe everything”: a failure or an infection must not touch the backups.
  • Archive copies that allow a fast recovery.
  • No upfront RDS licence purchase — monthly rental instead.
  • Nothing the client didn’t ask for: ask first, then do.

The solution

The working environment lives in a separate virtual machine. The physical server only runs it and stores its copies.

Diagram: the physical server, the virtual machine and who reaches them, and how

Employees and the admin each connect through their own VPN. The server can’t be reached from the internet, and copies are kept both on the host and in the cloud.

Why it’s built this way:

  • A virus can’t wipe everything. If something gets into the VM, the host and the nightly copies stay intact. Rolling back to yesterday takes 2 minutes.
  • Two separate VPNs. An employee’s access leads only to their desktop. There’s no path from it to server management.
  • Printing through RDP itself. A printer at home or in the office works without a single driver on the server.

Development story

The stages go in the order things actually happened: what went wrong and how we fixed it. Every change was made with PowerShell scripts, each with a backup of the current state and a separate rollback script.

ops(infra)#01

A new server and the foundation

We rented a physical server in a data centre in the Netherlands, with licences rented monthly. The disks were set up as a RAID-1 mirror, and the boot partition was synced so the server still starts if one disk dies. The hardware runs only Hyper-V; all the users’ work lives inside the virtual machine.

fix(network)#02

An IP address that already belonged to someone else

What happened: the virtual server couldn’t get online with the address the provider assigned. It turned out another customer’s server was already using that IP.

How we fixed it: gathered evidence with a script, contacted support, got a new address and moved the server to it.

sec(firewall)#03

Windows opened the door on its own

What happened: after Hyper-V was installed, the system exposed management services to the entire internet. The Remote Desktop and licensing rules were also set to “allow from anywhere”.

How we fixed it: admin access only through a separate VPN, desktop access only through the employees’ VPN. On top of that, emergency block rules in case someone opens a port by mistake. External scan: zero open ports.

sec(host)#04

Third-party software on the host

What happened: a few days later, two remote-access programs appeared on the physical server, installed without approval. They were a door around the whole VPN.

How we fixed it: saved the logs, found out who installed them and why, and removed them down to the last file. Then explained in plain words why the host must run nothing but virtualisation.

feat(monitoring)#05

A watchdog and one-click server status

So the next incident wouldn’t depend on luck, I set up an hourly watchdog on the host and the VM. It compares admins, services, scheduled tasks, firewall rules and logins against a baseline. The admin’s desktop has an “SRV status” shortcut: disks, backups, VPN, alerts and unknown logins on one screen.

feat(onboarding)#06

Onboarding employees without calling the admin

Solution: a generator creates a user in one run and packs an archive for them: a VPN key, a one-click installer, a shortcut and instructions. The person unzips it, runs it and starts working.

The catch: a fresh Windows update started showing a scary “unknown remote connection” warning and disabling clipboard and printers by default. Instead of turning that protection off, I signed the shortcuts with my own certificate, and the installer adds trust for it. Employees see no warning at all.

feat(print)#07

Printing without drivers

The employee’s printer is redirected to the server through Remote Desktop itself and works like a local one. Not a single driver is installed on the server, and the receipt printer in the other office prints the same way.

feat(backup)#08

Backups that actually restore

Solution: a nightly export of the whole virtual machine: 7 copies, disk integrity checks, protection against filling the disk. Inside the server, file shadow copies three times a day, so a deleted document can be recovered in a minute. AppLocker runs in audit mode to map what gets launched before switching to blocking.

The catch: making a copy isn’t enough — you have to restore it. A test restore showed that the export saves the machine’s memory state, and a second copy doesn’t fit into the host’s RAM. We worked out a procedure, and a restore now takes 2 minutes.

fix(app)#09

"Server is already running" in the accounting app

What happened: employees couldn’t see the database the manager created, or got “server is already running”. The cause: the app’s server part started under whoever logged in first, and each session saw its own separate instance.

How we fixed it: a dedicated service account for the app server and correct launch permissions. We explained the mechanics to the client’s manager in plain words, and he applied it himself.

fix(perms)#10

"Access denied" from the eighth user on

What happened: new employees got an error when starting the app. It turned out the settings file had been created by a different user first, and nobody else could write to it.

How we fixed it: write access to that one folder went to the employees’ group rather than to individual people. Every new user gets it automatically, and the app’s own files stay write-protected.

fix(dpi)#11

Scaling, borders and invoice printing

What happened: on laptops with 125–150% scaling, invoices printed too small. The first fix forced 100%, and printing became normal. But two new pains appeared: the server opened in a window with scrollbars, and people with poor eyesight couldn’t enlarge anything.

How we fixed it: we stopped treating everyone and treated one program: DPI compatibility mode was enabled for the accounting app on the server. Shortcuts went back to the PC’s own scaling: the server opens full-screen, everyone keeps their scale, and printing stays accurate. Verified by comparing PDFs before and after, without a single printer at hand.

sec(audit)#12

An audit after someone else's changes

What happened: when the client fine-tuned the database, the watchdog reported dozens of changes. Three were dangerous: an employee with admin rights; copies of the whole database readable by all 19 people; a system service folder where anyone could drop a file and become admin. The app’s firewall rules were set to “from anywhere”.

How we fixed it: first an audit with no changes, then the client’s sign-off on each item. Everything was closed with one script that backs up permissions and can roll back, and right after that we checked that the app and printing still work.

fix(backup)#13

A cloud database backup that only pretended to be green

What happened: the client’s script for copying the database to the cloud formally worked, and the monitoring service showed green. In reality, the task only ran while an admin was logged into the server. The path to the cloud client was wrong, and the script waited 15 seconds for the cloud sign-in instead of 2–3 minutes. On top of that, the cloud client locked the log file, so the next backup would have failed.

How we fixed it: the task runs without a logged-in user and without a stored password. The cloud client starts separately, and the script waits up to 4 minutes for the sign-in. The client’s script was edited surgically, with a copy of the original. Test: archive created, sync running, and the green in monitoring is now earned.

Lessons

The main thing I took from this project: check the end of the chain, not the green checkmark.

  • The host is not a workstation. The physical server runs only virtualisation, backups and the watchdog. Any extra program there is a door around every lock.
  • Every change comes with a backup and a rollback. Working remotely on someone else’s live server, it’s the only way to sleep well.
  • A restore must be tested, not assumed. The very first test found a problem that would have cost hours during a real outage.
  • Permissions go to groups, not people. Then the twentieth employee works exactly like the first.
  • Fix the program, not the people. Forcing one scale for everyone removed one problem and created two new ones.
  • Don’t switch protection off — do it properly. Signed shortcuts instead of disabling Windows warnings; a password-less task mode instead of someone’s stored password.
  • Don’t do what wasn’t asked. Suggest — yes, but the client decides. That’s what keeps trust.

Result

The office runs on the new server: 19 people connect with one icon from home or the office, printers print, and the server is invisible from outside.

  • Backups exist on three levels: the whole server nightly, files three times a day, the database daily to the cloud.
  • Adding a new employee is one script run and a ready archive.
  • The admin sees the state of everything in one click, and the watchdog reports any unexpected change.