Config Profile: Blocking AI Coding Assistants in VS Code

Complete
Author July 2026

Overview

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 are not approved. It is the enforcement layer behind an AI tool policy, as opposed to a policy document nobody reads.

The problem

AI coding assistants are the fastest-moving shadow IT category I have 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 is no download to block, no unapproved app in /Applications, and no network egress that stands out from ordinary IDE traffic. The endpoint controls that catch unmanaged software do not see an editor extension.

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, which is 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 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 do not load. Approved assistants are simply absent from the deny list, so sanctioning a new one is a one-line change and not 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 does not send your source anywhere, and it still writes code nobody evaluated into a repository. The approval question is whether the tool has been reviewed.

GitHub Copilot is deliberately absent from the deny list, because it is the sanctioned tool. That is 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.

TelemetryLevel = off cuts editor telemetry independently. That is a separate data-egress path that has nothing to do with extensions and is worth closing while you are here.

ExtensionsAutoUpdate = on with UpdateMode = start keeps permitted extensions current without prompting anyone. Extensions are a real supply-chain surface, and stale ones are their own problem.

The tradeoff

This is enforcement by identifier, so a determined person can evade it: publish under a different name, use a fork, use the web UI. It is not a hard security boundary and I would not present it as one.

What it does is make the unapproved path deliberate. It stops accidental adoption, which is most of the 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.

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 is installed so the control can be verified and not 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, and managed preferences were the only place the control could sit and still be about what people were doing.