Shore Up
A hand feeding a stack of paper name-slips into a sorting machine that drops each slip into its own labelled pigeonhole on a wall of mailboxes.
MailLinux

Bulk-Create Zimbra Accounts from a CSV with zmprov

Ketan Aagja8 min read
No ratings yet

Before you run this

This procedure reads a CSV of new users and generates a batch file of createAccount commands that zmprov executes to create real, live mailboxes on your Zimbra server. Creating accounts is a provisioning action that touches your directory and mail store. It is not destructive on its own — but the rollback (deleting an account) is irreversible: deleteAccount removes the mailbox and all of its mail with no undo. Treat the delete step accordingly.

Privileges: zmprov must run as the zimbra system user, not as root and not as your login account. On most installs you become that user with su - zimbra (which itself needs root). You do not run zmprov under sudo.

Test first. Read the generated batch file before you feed it to zmprov — it is plain text and you can eyeball every line. Run it against one test row first (a single throwaway address like zztest@example.com), confirm the account looks right, delete it, and only then process the full list. Do this on a staging server if you have one. Do not paste a 500-line batch at a production server you have never run this against.

One more thing: the CSV and the generated batch file both contain plaintext passwords. Keep them out of world-readable locations, and delete them when you are done (I cover this at the end).

Assumptions

  • Zimbra Collaboration (open-source or network edition), 8.8/9.x style, installed under /opt/zimbra.
  • A single-server or standard multi-server setup where zmprov on this host can create accounts.
  • A Linux host (the paths and the zimbra user are the same on Ubuntu and RHEL/Alma).
  • Your CSV has simple values with no embedded commas or quotes in the fields. If your data has commas inside names, a while read split on commas will mangle it — use a real CSV parser (Python's csv module) instead.

The CSV format

One header row, then one account per line:

email,password,firstName,lastName,displayName
alice.smith@example.com,ChangeMe-8f3Kd,Alice,Smith,Alice Smith
bob.jones@example.com,ChangeMe-2p9Rt,Bob,Jones,Bob Jones

Replace example.com and the sample rows with your own. Every email address must be in a domain that already exists in Zimbra — createAccount will fail for a domain you have not created with zmprov createDomain. Passwords must satisfy the password policy on the account's Class of Service (COS); if your COS enforces length or complexity, weak passwords will be rejected and that row will fail.

Step 1 — Generate the zmprov batch file

The efficient, well-established way to create many accounts is to build a file of zmprov commands and feed the whole file to a single zmprov process. That avoids paying the JVM startup cost once per account.

Save this as make-batch.sh. Run it as your normal user (it only reads the CSV and writes a text file — it does not touch Zimbra yet).

#!/bin/bash
# make-batch.sh — turn accounts.csv into a zmprov batch file.
# Does NOT create anything; it only writes commands for you to review.

set -euo pipefail

CSV="${1:?Usage: $0 accounts.csv}"
BATCH="./zmprov-batch.txt"

: > "$BATCH"                      # start with an empty file
chmod 600 "$BATCH"               # it will hold plaintext passwords

# Skip the header (tail -n +2), then read comma-separated fields.
tail -n +2 "$CSV" | while IFS=, read -r email password first last display; do
    [ -z "$email" ] && continue  # skip blank lines

    # createAccount <email> <password> [attr value ...]
    # Quote the space-bearing display name so zmprov sees one value.
    printf 'createAccount %s %s givenName "%s" sn "%s" displayName "%s"\n' \
        "$email" "$password" "$first" "$last" "$display" >> "$BATCH"
done

echo "Wrote $BATCH ($(wc -l < "$BATCH") accounts). Review it before running."

givenName, sn, and displayName are standard directory attributes Zimbra understands. You can add others the same way (company, title, telephoneNumber) — check the account attributes in the Zimbra documentation for the exact names before adding them; don't guess.

If you want to force users to change the password at first login, append the attribute Zimbra uses for that (zimbraPasswordMustChange TRUE) to the printf line. Confirm the attribute name against the Zimbra admin documentation for your version before relying on it.

Run it and read the output:

./make-batch.sh accounts.csv
cat zmprov-batch.txt

You should see one clean createAccount … line per user. Fix the CSV and regenerate if anything looks off.

Step 2 — Try one account first

Before the full run, prove the format works. Copy a single line into its own file:

head -n 1 zmprov-batch.txt > zmprov-test.txt

su - zimbra
zmprov < /path/to/zmprov-test.txt   # replace with the real path

zmprov reads commands from standard input, one per line, and executes them. If the account is created with no error, the format is good. Verify and delete this test account (see verification and undo below), then move on.

Step 3 — Run the full batch

Still as the zimbra user:

zmprov < /path/to/zmprov-batch.txt   # replace with the real path

zmprov processes each line. If one account fails (duplicate address, policy-rejected password, unknown domain), it prints an error for that line and continues with the rest. Read the whole output — don't assume silence means success; watch for ERROR lines and note which addresses they belong to so you can fix and re-run just those.

Verify it worked

Check a specific account exists and has the attributes you set:

zmprov ga alice.smith@example.com displayName givenName sn

ga is getAccount; it prints the account and the attributes you name. To see the accounts in a domain:

zmprov -l gaa example.com

gaa is getAllAccounts. Spot-check that the new addresses are present and count roughly matches your CSV. Finally, confirm a user can actually log in to the web client with the password from the CSV.

Undo / rollback

Deleting an account removes the mailbox and its contents permanently — there is no recycle bin. Only do this for accounts you genuinely want gone (for example, cleaning up after a bad test run).

To remove a single account:

zmprov deleteAccount zztest@example.com   # 'da' is the short form

To roll back a whole batch, generate delete commands from the same CSV and — after reading the file — feed it to zmprov. Note the double confirmation baked in: you review the file before running.

# make-delete.sh — build deleteAccount commands from the same CSV.
tail -n +2 accounts.csv | while IFS=, read -r email _; do
    [ -z "$email" ] && continue
    echo "deleteAccount $email"
done > zmprov-delete.txt

cat zmprov-delete.txt        # READ THIS. Every line destroys a mailbox.
# Only when you are sure:
#   su - zimbra
#   zmprov < /path/to/zmprov-delete.txt

I have deliberately left the actual zmprov < … delete line commented out. Uncomment and run it by hand once you have read the file and accepted that it is irreversible.

Clean up the plaintext passwords

The CSV and both batch files contain passwords in the clear. When provisioning is done and verified, remove them:

rm -f accounts.csv zmprov-batch.txt zmprov-test.txt zmprov-delete.txt

If you must keep the CSV as a record, at least strip the password column first and store it somewhere with restricted permissions.

For exact command syntax and the full list of createAccount attributes, see the Zimbra Collaboration administration documentation for zmprov rather than trusting any option you are unsure about.

Written by
Ketan Aagja

Runs enterprise networks and security for a living, and writes Shore Up to turn two decades of hands-on Linux, Windows and mail-server work into guides you can actually use.

More about the author →

Was this article helpful?

Tap a star — no sign-in needed.

Be the first to rate this article.