Workflow IA

Fifteen student accounts in a single command

Every September, two hours of clicking through cPanel to set up my students' hosting. SSH access, a command-line API, a skill tested and fixed - it now fits in one command.

The goal: never again spend a back-to-school evening inside a hosting control panel. Fifteen students, five operations each, a web interface that takes six clicks per operation. The kind of task where you are guaranteed to slip up by the twelfth student.

I teach web development. Every student gets a personal LAMP hosting space where they publish their work, online, under their own subdomain. That is the heart of the setup: you don't learn the web locally, you learn it by publishing. The catch is that every September, this setup cost me half a day of clicking.

The problem

A student isn't one account. It's five things to create, in the right order, following the right conventions:

  • A folder - filed by class year, with the right permissions
  • A subdomain - firstname.mydomain.be, pointing at that folder
  • An SSL certificate - because a plain HTTP site in 2026, no thanks
  • A MySQL database - with its own user and privileges
  • An FTP account - locked to their own folder

My hosting is a standard shared plan with cPanel, the interface you'll find at a good share of hosting providers. It does the job. But it does it with a mouse: every operation means navigating a menu, filling in a form, confirming, going back. Count five to ten minutes per student when nothing goes wrong.

Fifteen students works out to somewhere between an hour and a half and two and a half hours. Twice a year. Repeated in exactly the same way, without mistyping a single character in a database name.

And that's where it really hurts: it isn't long, it's mind-numbing. The kind of task where your attention slips at the seventh student and where, three weeks later, you find out someone's FTP account points at the wrong folder.

What I already had

SSH access. Just about every serious shared host provides it, and I only ever used it for the occasional bit of troubleshooting.

Yet cPanel exposes a command-line API, uapi, reachable from that SSH session. Everything the web interface does with forms, this API does with a command. I vaguely knew that. I had never explored it, because the entry cost (reading the docs, testing, getting it wrong) always outweighed the cost of an evening of clicking.

That is exactly the calculation AI changes.

The exploration

I asked Claude Code to explore the server and tell me what could be driven from SSH. Not to write anything: just look, list the available functions, work out the conventions already in place across my previous class years.

The result, within minutes:

OperationCommandStatus
Create a subdomainSubDomain addsubdomainOK
Create a databaseMysql create_databaseOK
Create a SQL userMysql create_userOK
Create an FTP accountFtp add_ftpOK
Delete a subdomaindelsubdomainUnavailable

Four operations out of five automatable straight away. The fifth, deleting a subdomain, only exists in an older version of the API whose binary is hidden by the shared-hosting isolation layer. It stays manual. No great loss: we create fifteen accounts a year and delete them far less often.

SSL holds out. Automatic renewal isn't enabled on my account, and the plugin that issues certificates only exists in the web interface. When a subdomain is created, the certificate installed is self-signed: the browser shows a warning until you issue the real one. It's the one step I still do by hand, but the interface lets you tick every subdomain at once: one minute for the whole class, against five to ten minutes per student before.

Don't stop at the script

At that point I had enough to write a shell script and move on. That is what I would have done two years ago, and the script would have ended up forgotten in a folder, undocumented, unusable by the following September.

The real unit of reuse today isn't the script. It's the skill: a folder holding both the script and the instructions, written for an AI to follow. The difference is concrete. Next September I won't have to find the script or remember its syntax. I'll write:

Create the accounts for class 3tiweb2028:
martin, leila, kwame, sofia, ...

And the skill handles the rest, including reminding me of the conventions I'll have forgotten.

Writing the skill the way you write tested code

A badly written skill is worse than a script: it creates the illusion that the knowledge is captured when it is actually incomplete. So I applied the counter-intuitive method: watch it fail before writing the solution.

I launched an agent with no documentation at all, told it to change nothing, and asked how it would go about the task. Its gaps became my outline:

  • It forgot the FTP account - it hadn't guessed that step, even though it's what lets the student upload their files
  • It hesitated over naming conventions - two schemes coexisted across my older class years
  • It couldn't tell whether the parent folder had to exist - precisely the case for the year's first class

Once the skill was written, I put it in front of another agent and asked it to be harsh. Verdict: 6.5 out of 10, with a precise list of flaws. No connection command, no loop to handle a list, security documented but missing from the procedure, passwords generated then lost for want of capturing them.

That score saved me more time than any review of my own. I know the subject too well to see what's missing.

The two bugs only real execution revealed

With the skill fixed, I tested it on a throwaway account. Two bugs surfaced, both invisible on the page.

A character range instead of a list

The password generator used this character class:

tr -dc 'A-Za-z0-9!#%*+-=?@'

The first password it produced contained an angle bracket: lKlA=JTo<w1VtfpUiWtJ. But the angle bracket isn't in my list.

Here's why: inside a character class, +-= isn't three characters but a range, running from + to = in the ASCII table. It includes the angle bracket, the slash, the comma. Exactly the characters that break a shell string or a connection URL. The hyphen has to go last to be taken literally.

An optional parameter that isn't

Creating the FTP account returned a clean success. But the login actually created wasn't firstname@mydomain.be: it was firstname@accountname.host.fr, attached to the primary domain.

Without an explicit domain= parameter, cPanel picks the account's primary domain. The trap is nasty: the command succeeds, nothing raises a flag, and the mistake only shows up when the student tries to log in with the credentials you handed them. Worse, deletion then fails with "this account does not exist", since you pass it the expected domain rather than the one actually registered.

Neither bug was visible on review. Both would have blown up mid-September, on real accounts, in front of students who are waiting. Testing on a throwaway account and then deleting it costs five minutes.

What it looks like now

The skill is 232 lines of documentation and 146 lines of script. One command, one list of usernames:

bash provision.sh 3tiweb2027 martin leila kwame

Output per student:

--- martin ---
  [1/5] subdomain    OK  martin.mydomain.be
  [2/5] permissions  OK  750 nobody
  [3/5] database     OK  account_martin
        mysql user   OK  account_martin
        privileges   OK
  [4/5] ftp          OK  martin@mydomain.be
  [5/5] hardening    OK  .htaccess in img/ uploads/ files/

And on standard output, one CSV line per student, ready to become the table I hand out: credentials, URL, database, FTP login, unique password. Diagnostics go to standard error, which means you can redirect the CSV into a file without polluting it.

BeforeAfter
Per student5 to 10 minutesa few seconds
For 15 students1h30 to 2h30one command
Risk of errorhigh, and rising with fatiguenone, checked at every step
Credentials tablecopied by handgenerated

The limits

Honest automation states what it does not do.

  • The SSL certificate stays manual - the plugin that issues it only exists in the web interface, but one minute covers the whole class
  • Deleting a subdomain stays manual - the function isn't exposed in the API reachable from shared hosting
  • A mid-run failure leaves a partial state - the script stops dead and says so, but doesn't undo what it already created
  • The script refuses to overwrite an existing account - that's deliberate, but it means recovering from a failure takes a manual cleanup
  • Nothing replaces checking - I always look at the first account created before launching the other fourteen

The takeaway

What strikes me isn't the raw time saved. It's that this automation had been within my reach for years. The API existed, the SSH access existed, the skills existed. What was missing was the willingness to spend an evening reading API documentation to save an evening of clicking. The maths never tipped the right way.

AI invented nothing here. It collapsed the cost of exploration. Reading the docs, testing the commands, discovering that a parameter is mandatory when it looks optional: that groundwork, which used to cost me an evening, now takes a few minutes.

It isn't magic for all that. The skill's first draft scored 6.5 out of 10. The two most annoying bugs only surfaced during real execution, on a real server. I was the one who knew which conventions to follow, what the right database name was, why the permissions had to match those of the earlier class years.

AI is excellent at repetitive, tedious tasks. Not because it does them better than I do, but because it makes automating them worth the effort once and for all.

The question is no longer "can I automate this?". It's "is it worth it?". And the answer has changed.

What I'm taking away

  • The skill, not the script. A script without instructions is lost by the next school year
  • Watch it fail before writing. The gaps of an undocumented agent make the best outline
  • Have your own work graded. A well-argued 6.5 out of 10 beats a flattering review
  • Test for real, on something disposable. The costliest bugs aren't visible on the page
  • Automate what's tedious, not what's hard. Mind-numbing tasks are where humans slip