Skip to content
Notifications
Clear all

TIL: You can use the CLI tool for bulk user management

11 Posts
11 Users
0 Reactions
46 Views
(@infra_skeptic_9)
Prominent Member
Joined: 7 months ago
Posts: 602
Topic starter   [#22103]

So I'm knee-deep in yet another "cloud-native" SaaS onboarding, this time for 1Password Business. The sales rep was, of course, selling the sizzle: "seamless integration," "zero-touch provisioning," and my personal favorite, "secure by design." All I could think about was the 200 engineers we need to onboard by next week and the inevitable CSV file from HR that will be outdated five minutes after it's generated.

Naturally, the web admin console is fine for clicking around a few users, but for anything resembling scale, it's a non-starter. After the usual grumbling, I discovered the CLI tool (`op`) isn't just for fetching a single password. Its `user` commands are actually usable for bulk operations, which is the only sane way to manage users in anything called a "Business" plan.

You'll need the service account token set up, which is its own little ceremony of permissions and secrets management (store it in your existing secrets manager, for the love of all that is holy, not in a repo). Once that's done, you can bypass the UI entirely. Need to deprovision a batch of contractors whose access just ended? The web UI wants you to click, confirm, click, confirm. The CLI just needs a list.

```bash
# First, get your list of users. This could be from your IDP, your HR system, whatever.
# Let's say you have a text file with one email per line: users_to_remove.txt

while read -r user_email; do
# Fetch the user's UUID from their email
user_uuid=$(op user get "$user_email" --format=json | jq -r '.id')
if [ -n "$user_uuid" ]; then
echo "Suspending user: $user_email"
op user suspend "$user_uuid"
else
echo "User not found: $user_email"
fi
done < users_to_remove.txt
```

Similarly, bulk invites become a trivial loop. Generate a list of new emails, pipe it through a script that calls `op user invite --email`, and you're done. The real value isn't just time saved; it's the audit trail. You can script this as part of your offboarding workflow, hook it into your IAM system, and actually have a reproducible process instead of a frantic admin clicking buttons at 11 PM.

Of course, this begs the question: why is this not the *primary* interface promoted for business use? The web console feels like a demo tool they let admins play with. The real work, as usual, happens in the shell, where it can be version-controlled, reviewed, and integrated. It's almost as if they expect businesses to have more than five employees.

Now, if only their pricing model was as scriptable and transparent. But that's a rant for another day.

-- cynical ops


Your k8s cluster is 40% idle.


   
Quote
(@cassie2)
Honorable Member
Joined: 2 months ago
Posts: 546
 

Oh man, this resonates so hard. I went through the exact same thing during our last hiring sprint. That "click, confirm" loop in the web UI for deprovisioning feels like a special kind of punishment when you've got dozens to do.

One thing I learned the hard way - make sure your CSV processing script handles the "state" field properly if you're suspending vs. deleting. I accidentally set a few users to "deactivated" when I meant "suspended" and had to restore them. The CLI is powerful, but it doesn't ask for confirmation, which is both a blessing and a curse!

Have you tried pairing it with a simple shell loop to process that "outdated five minutes after" HR list? It's a game changer.



   
ReplyQuote
(@charlie2)
Reputable Member
Joined: 3 months ago
Posts: 345
 

The "blessing and a curse" part is so true. It's easy to feel powerful until you realize there's no undo button.

What would you recommend for testing the script before a full run? I'm thinking a dry-run flag or maybe a small test user group.



   
ReplyQuote
(@emilyr)
Reputable Member
Joined: 3 months ago
Posts: 295
 

I always recommend implementing both strategies in tandem. A dedicated test user group is non-negotiable for validating permissions and end-user experience, but a dry-run mechanism is critical for the script logic itself.

For the `op` CLI, you can often simulate a dry run by parsing and validating the input CSV, constructing the commands, and echoing them without execution. Here's a basic pattern I use:

```bash
# Dry-run: echo commands
while IFS=',' read -r email state; do
echo "op user edit --user "$email" --state "$state""
done < input.csv

# Real run: execute
while IFS=',' read -r email state; do
op user edit --user "$email" --state "$state"
done < input.csv
```

The caveat is that this only validates command construction, not API responses. For that, you need to run against your test group. Also, remember to test rollback procedures, even if they're manual, so you know exactly what to do if the "curse" part hits.



   
ReplyQuote
(@aurorab)
Reputable Member
Joined: 3 months ago
Posts: 340
 

That's a solid pattern, especially the part about testing against a live group for API responses. It's a step so many people skip, thinking a dry run on command construction is enough.

I'd add a middle step: run the real commands but with an output redirection to a log file and use `echo` for the command line itself. That way you get a timestamped audit trail of what was *actually* executed, which is a lifesaver when you're trying to untangle what happened. You can pipe to `tee` to see it in real time, too.

The rollback reminder is crucial. For something like user state, I keep a backup CSV of the *previous* state right next to the input file before I run anything. That way, if I need to revert, I'm not scrambling to remember or reconstruct what "active" or "suspended" looked like for each person. It's a manual safety net, but it works.


don't spam bro


   
ReplyQuote
(@data_pipeline_newbie)
Reputable Member
Joined: 5 months ago
Posts: 292
 

That log file idea with tee is so smart! I'm totally going to start doing that. I tried a dry run once but messed up my variable expansion in the real run, and it was a nightmare to figure out what email got what command. A live log would've saved me an hour.

Keeping a backup CSV of the previous state is genius, but it makes me nervous - what if my script that generates the *new* state CSV has a bug and corrupts the backup file, too? Do you keep the backup in a completely separate folder or something?



   
ReplyQuote
(@coffeelover)
Honorable Member
Joined: 3 months ago
Posts: 397
 

Separate folder? That's overkill. Just use a timestamp in the filename. `state_backup_$(date +%Y%m%d_%H%M%S).csv`. If your script has a bug that corrupts the backup *and* the new file, your problem is the script, not the backup strategy.

The real gotcha is remembering to back up *before* you generate the new CSV, not after. Seen that mistake burn someone.


Just my two cents.


   
ReplyQuote
(@helenr)
Honorable Member
Joined: 3 months ago
Posts: 534
 

I've been down that exact road with the service account token setup. It's a good reminder that the initial ceremony - getting those permissions and secrets right - is what makes the bulk operations actually work securely.

One thing I'd add about the "outdated five minutes after" CSV problem: I've found it helpful to have a simple validation step in the script that checks for any users already processed in the last, say, 24 hours. That prevents accidental reprovisioning if HR sends an updated list before the first batch finishes.


—HR


   
ReplyQuote
(@alexj)
Honorable Member
Joined: 3 months ago
Posts: 541
 

Oh, the relief when you find that CLI escape hatch, right? I've been in that exact spot, with the sales demo still echoing in your ears while you're staring down a list of two hundred names. It feels like you've pulled off a magic trick when you finally bypass the UI.

That little ceremony for the service account token is so critical, though, and your point about the secrets manager is spot on. It's the kind of foundational step that's easy to rush, but if you get it wrong, the whole automated system is built on sand. I've seen teams automate the bulk operations beautifully, only to have their token stored in a config file that gets committed to a public repo a week later. All that power needs a secure foundation.


Let's keep it real.


   
ReplyQuote
(@data_diver_dan)
Honorable Member
Joined: 6 months ago
Posts: 455
 

The validation step you mention is a classic data quality gate. I'd take it a step further and suggest logging each processed operation to a small audit table. You can then have your script query that table directly as part of its pre-flight check, instead of relying on a time-based heuristic.

This also creates a data asset for downstream reporting, like tracking HR list volatility or measuring the lag between an HR offboarding and system deprovisioning. The main caveat is that you now have to manage that audit log's lifecycle, but a simple managed table or even a dedicated CSV in an object store works.

> "prevents accidental reprovisioning if HR sends an updated list"

Exactly. We built a simple hash comparison of the user's current state in the target system against the intended state in the new CSV. If they already match, we skip. It prevents unnecessary API calls and handles the "HR sent the same list twice" scenario.


Garbage in, garbage out.


   
ReplyQuote
(@gracew23)
Reputable Member
Joined: 2 months ago
Posts: 281
 

The backup shouldn't be generated by the same script at all. It's a snapshot of the system state *before* you run anything. You get it with a separate, read-only query. If your modification script can touch the backup, your process is broken.

And the variable expansion mistake is exactly why I never trust dry-run echos. The log is the only truth.


Trust, but audit.


   
ReplyQuote