Developer Notes

Closeup of a container with a padlock. There is colorful graffiti on the container. Picture by Fer ID on Pexels.

Your agent inherits Everything you forgot to log out of

I spent a few days at a customer to hammer out some new features and they gave me access to their Azure environment to do the work, including their production database. It made sense for the work, so I accepted that. Then I went back to the Zure office, opened my laptop, and carried on with something else.

I was still logged in to their tenant... on the same machine where I run coding agents every day.

Luckily nothing happened, because I quickly caught my own mistake. Thing is... nobody was careless, no policy was broken, nothing was misconfigured. I had legitimate access, I used it for legitimate work, and then I changed context and the access just… stayed. And if an agent had done something with it, nothing in my setup would have prevented it or warned me.

My colleague Geert van der Cruijsen wrote about this last week and ended with an open question: how do you box this in? This post is part of an answer.

The Step We Never Take

We've been applying least privilege for humans for years, and we're reasonably good at it. You get the access you need, when you need it. Access expires and all is good. This is why Just-in-Time access solutions like PIM exist. We built tooling specifically because people tend not to revert to lower privileges afterwards.

Now look at your development laptop. az login doesn't end when you switch context. It's a user level login, persisted across reboots. You don't log out of these sessions the way you hand back a visitor badge at reception.

So the risky state isn't being granted access. It's retaining it after the thing that justified it is over. That gap used to be mostly theoretical — a session sitting idle isn't doing anything. It's a different proposition once something else on the machine can pick it up and act.

What an Agent Does With It

An agent can't tell the difference between "credentials that happen to be in this environment" and "credentials meant for this task". Smarter harnesses may do some quick checks before deploying but will almost certainly not alert you that you're still logged in to that admin account you switched to before lunch. It runs where you run, with what you have, and if az is authenticated then az works.

Two things make that worse than the equivalent human mistake. Agents move fast, across many steps, and a wrong one propagates before you'd notice. And when something does go wrong, you may not spot it until it is too late.

One Login Store

Here's the part that took me longest to see, and it's the reason "just give your agent its own identity" is easier said than done.

Your machine has one ~/.azure folder. One login store, one active identity. You cannot be your admin self on the host and a restricted agent identity at the same time — the second az login overwrites the first. The machine structurally can't hold the separation.

Giving an agent a lower-privilege account isn't something you can simply decide to do within the context of a dev box. There's nowhere to put it. And that reframes my near-miss: the customer session wasn't dangerous because it was over-privileged. It was dangerous because it was the only login on the machine, so it was the one any agent would find. There was no way to scope that identity to the task at hand.

The Pattern, In Order

So the fix has two halves, and they're additive.

First, isolate the runtime state. Run the agent in a container that has its own Azure CLI state, separate from the host's. On its own this reduces exactly zero privilege — it's not a security boundary yet. What it does is create room for a second identity to exist. Host and container can now hold different logins.

Then put a scoped identity in that room. A dedicated low-privilege account for agent work, granted what the task in front of it needs and not the rest.

Step one without step two is a container with your credentials in it; it helps scope the access to a tenant or subscription but it's not a security boundary. A rogue agent may be able to switch subscription or even tenant.

Assigning low-privilege accounts to agents without containment puts the burden on you to switch accounts and prevents multitasking between tasks with different identities.

The two parts are better together.

Why a Container Instead of a VM

Another solution is to run agents in a cloud VM, and it's a genuinely stronger boundary. It's also a VM: something to provision, patch, pay for while it idles, and reach across to get at the code you're working on. It allows you to close the lid on your laptop in exchange for more surface area to maintain and protect. I've seen people run these without proper network security, almost begging to get breached.

A Dev Container costs almost nothing. It never leaves your machine, which fits nicely with common IT practices in larger organisations. A lot of teams onboard onto a repo this way already, and because the container definition lives in the repo, everyone who clones it inherits the setup instead of being asked to adopt a practice.

To be clear about what you're getting, though: a container is the weaker boundary of the two. It's not a sandbox for hostile code, and if you mount your host's home directory into it you've undone the entire point. 

Downsides

Containers are not entirely free of course, you'll need to build the image and run it at the expense of system resources. Particularly file system access on Windows and MacOS can be slower.

Any time you start a fresh dev container you'll need to authenticate all CLIs you want to use. Git is taken care of by the dev container though.

There is also a more subtle issue for agentic AI; project and user memory is stored in your home directory alongside the cloud credentials you didn't want to share with the agent. The agent folders can be mounted into the dev container, but you'll have to carefully pick the folders to prevent any credentials from sneaking in through the back door. If you want to keep track of conversation history across dev container instances, you'll need some other way to do that.

The Pudding

As an example, I've defined dev containers in my azure-egress-proxy repo. It's a .NET 10 development container with the toolchain that project needs: Azure CLI, the Aspire CLI, Docker-in-Docker so Aspire can start workloads locally, and support for linked worktrees.

Summary

An agent running on your machine has whatever credentials your machine has. The problem isn't the access you deliberately grant it, but the access you forgot was still there, because local CLI sessions have no ending and nothing prompts you to close them.

Devcontainers don't fix that by themselves. They make the fix possible by giving additional credentials somewhere to live, alongside the work you're doing. The next step is to actually create an identity with least privilege for your agents.

General advice

  • If you run agents on your dev box, the agent has everything you have.
  • Give agent workloads their own isolated workspace.
  • Then give them their own identity, scoped to the task, not your account.
  • Never mount your host credential directories into an agent's devcontainer for convenience. It undoes the whole thing.
  • Keep production out of scope for anything running locally. If an agent needs prod, that's a pipeline, not a laptop.
  • Treat customer access as something you actively hand back. Nothing else is going to do it for you.

No comments yet

Leave a comment

Leave a comment

Please enter your name.
Please enter your e-mail address.
Please enter your comment. You may use basic HTML formatting.

Thank you!

Your comment is submitted and I'll publish it asap.