Config Profile: Blocking AI Coding Assistants in VS Code
CompleteOverview
A configuration profile in the com.microsoft.VSCode domain that enforces which extensions the editor will load. It allows extensions in general and explicitly denies AI coding assistants that aren’t approved — the enforcement layer behind an AI tool policy, rather than a policy document nobody reads.
The problem
AI coding assistants are the fastest-moving shadow IT category I’ve dealt with. A developer installs one because it makes them measurably faster. That decision takes eleven seconds, involves no procurement, and quietly establishes a pipeline where proprietary source code is sent to a third party for inference.
Nothing about it looks like an incident. There’s no download to block, no unapproved app in /Applications, no network egress that stands out from ordinary IDE traffic. The endpoint controls that catch unmanaged software do not see an editor extension.
And a blanket ban is the wrong answer. Ban all AI assistance and you lose real productivity while pushing people onto personal machines and web chat windows, where you have no visibility at all — strictly worse than sanctioned usage.
So the requirement is specific: allow AI tooling we have reviewed, deny the rest, at the layer where the choice is actually made.
What the profile does
AllowedExtensions = {
"*" : true, // default permit
"openai" : false,
"Google.geminicodeassist" : false,
"AmazonWebServices.amazon-q-vscode" : false,
"sourcegraph.cody-ai" : false,
"Codeium.codeium" : false,
"saoudrizwan.claude-dev" : false,
"Continue.continue" : false,
"Ollama.ollama" : false,
… // 28 denials total
}
TelemetryLevel = off
ExtensionsAutoUpdate = on
UpdateMode = start
The deny list runs to 28 extensions and is maintained as the market moves — Copilot alternatives, agentic coding tools, the IDE-side companions for CLI agents, and a long tail of smaller assistants that appear faster than any procurement process could track them.
"*": true with named denials is the deliberate shape. A pure allowlist — deny everything, permit named extensions — would be more restrictive and completely unworkable: developers use hundreds of legitimate extensions, and IT becomes a bottleneck on every linter and theme. That model collapses into either a permanent approval queue or a blanket exception.
Denying by name targets the actual risk. The default stays permissive, so nobody’s workflow breaks, and the specific tools that would route source code to an unreviewed vendor don’t load. Approved assistants are simply absent from the deny list, which means sanctioning a new one is a one-line change rather than a policy rewrite.
Locally-hosted AI is denied too — Ollama and Continue are on the list despite being self-hosted. That looks inconsistent until you separate the two risks: data egress is one, and unreviewed code suggestions entering the codebase is the other. A local model doesn’t send your source anywhere, and it still writes code nobody evaluated into a repository. The approval question is “has this been reviewed,” not “does it phone home.”
GitHub Copilot is deliberately absent from the deny list, because it’s the sanctioned tool. That’s the whole shape of the policy in one detail: one reviewed, licensed, approved assistant is available to everyone, which is what makes denying the other 28 reasonable rather than obstructive.
TelemetryLevel = off cuts editor telemetry independently — a separate data-egress path that has nothing to do with extensions and is worth closing while you’re here.
ExtensionsAutoUpdate = on with UpdateMode = start keeps permitted extensions current without prompting anyone. Extensions are a real supply-chain surface; stale ones are their own problem.
The tradeoff I’d name out loud
This is enforcement by identifier, so it’s evadable by a determined person — publish under a different name, use a fork, use the web UI. It is not a hard security boundary and I wouldn’t present it as one.
What it does is make the unapproved path deliberate. It stops accidental adoption, which is the overwhelming majority of the actual risk: nobody sets out to leak source, they install a well-reviewed extension without thinking about where the context window goes. Removing the accident is most of the win, and the residual — someone actively working around a control — is a conduct question, not a tooling one.
Outcome
AI assistance in the IDE became a reviewed decision instead of a default one, with approved tools available and unapproved ones failing to load. Paired with the extension inventory attribute, which reports what’s actually installed so the control can be verified rather than assumed.
Lessons Learned
Govern the layer where the decision gets made. Network filtering and application allowlisting both sit at the wrong altitude for this — the choice happens inside an approved application, in a marketplace built for frictionless install. Managed preferences were the only place the control could sit and still be about what people were actually doing.