VSCode Dev Containers allow sandboxing GitHub coding agents and providing agents with the dependencies and tools they need to be successful. In containers, authentication with private Azure DevOps (ADO) artifact feeds can be challenging though. Containers typically don’t have a UI, let alone a browser, and interactive authentication requires showing a log-in page. This blog goes over how to streamline ADO authentication using Azure CLI when restoring packages from Nuget, Maven, or NPM. This leverages VSCode’s authentication proxy which lets Azure CLI in the container open an Entra log-in page on the host and get an access token back.
TLDR (with thanks to azure-cli-awesome): have Azure CLI authenticate with ADO, generate a Personal Access Token (PAT), and set the PAT in user package-manager configs.
az login
az rest --method post --uri "https://vssps.dev.azure.com/{org}/_apis/Tokens/Pats?api-version=6.1-preview" --resource "https://management.core.windows.net/" --body '{"displayName": "DevContainer", "scope": "vso.packaging"}' --headers Content-Type=application/json --query patToken.token --output tsvIn the code snippets, replace
{org}with your ADO organization and{feed}with your ADO artifact feed.
Storing PATs in user configs outside of code repos ensures those secrets do not end up checked-in by mistake.
For Nuget, start by creating a Dev Container config, e.g. /.devcontainer/devcontainer.json, that defines a custom Dockerfile plus the extensions, settings, etc. of your choice.
{
"name": "DevContainer",
"build": { "dockerfile": "Dockerfile" },
"customizations": {
"vscode": {
"extensions": ["ms-dotnettools.csdevkit"],
"settings": {
"chat.tools.autoApprove": true,
"chat.tools.terminal.autoApprove": {"/.*/": true},
"chat.tools.terminal.ignoreDefaultAutoApproveRules": true
}
}
}
}In /.devcontainer/Dockerfile, start with the official Dev Container image, install the Azure CLI, and pre-populate a user Nuget config.
FROM mcr.microsoft.com/devcontainers/dotnet:8.0
RUN curl -sL https://aka.ms/InstallAzureCLIDeb | bash
COPY <<EOF /root/.nuget/NuGet/NuGet.Config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="{feed}" value="placeholder" />
</packageSources>
<packageSourceCredentials>
<{feed}>
<add key="Username" value="DevContainer" />
<add key="ClearTextPassword" value="placeholder" />
</{feed}>
</packageSourceCredentials>
</configuration>
EOFFinally, create a Bash file at /.devcontainer/ado-login.sh that logs-in using Azure CLI, generates a PAT, and sets the PAT in the user Nuget config.
#!/bin/bash
set -euo pipefail
echo "Logging into Azure..."
az config set core.login_experience_v2=off
az login --allow-no-subscriptions -o tsv
echo "Generating Azure DevOps PAT token for NuGet..."
token=$(az rest --method post --uri "https://vssps.dev.azure.com/{org}/_apis/Tokens/Pats?api-version=6.1-preview" --resource "https://management.core.windows.net/" --body '{"displayName": "DevContainer", "scope": "vso.packaging"}' --headers Content-Type=application/json --query patToken.token --output tsv)
sed -i -E "s|<add key=\"ClearTextPassword\".+?/>|<add key=\"ClearTextPassword\" value=\"$token\" />|" /root/.nuget/NuGet/NuGet.ConfigFor Maven, follow a similar setup with the user config at ~/.m2/settings.xml.
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
https://maven.apache.org/xsd/settings-1.0.0.xsd">
<servers>
<server>
<id>{feed}</id>
<username>{org}</username>
<password>placeholder</password>
</server>
</servers>
</settings>For NPM, do the same with the user config at ~/.npmrc, after applying an extra base64-encoding to the PAT by piping it through | base64 -w0.
; begin auth token
//{org}.pkgs.visualstudio.com/_packaging/{feed}/npm/registry/:username={org}
//{org}.pkgs.visualstudio.com/_packaging/{feed}/npm/registry/:_password=placeholder
//{org}.pkgs.visualstudio.com/_packaging/{feed}/npm/registry/:email=not used
//{org}.pkgs.visualstudio.com/_packaging/{feed}/npm/:username={org}
//{org}.pkgs.visualstudio.com/_packaging/{feed}/npm/:_password=placeholder
//{org}.pkgs.visualstudio.com/_packaging/{feed}/npm/:email=not used
; end auth token