Azure 51: Azure Artifacts Maven/Gradle — `ado_artifacts_token` for backend libs
easy⏱ 5 mincourseazure
Why a build-arg
Maven/Gradle wants credentials at build time. If you bake them as ENV in the Dockerfile, anyone with docker inspect can read them. If you mount them at runtime, the build can't see them. The clean middle ground: take a build-arg (visible only during build), use it in a single layer, and never RUN echo $TOKEN (which would write it to a layer). Modern Docker BuildKit's --mount=type=secret is even better but the build-arg approach is what Lythia uses today.
# Dockerfile snippet
ARG ado_artifacts_token
RUN --mount=type=cache,target=/root/.gradle \
ORG_GRADLE_PROJECT_adoArtifactsPassword="$ado_artifacts_token" \
gradle bootJar --no-daemon
init.gradle — auth glue
Gradle reads auth from project properties (adoArtifactsUsername, adoArtifactsPassword). An init.gradle baked into the build image points repositories at the Lythia Maven feed and uses those properties. Setting ORG_GRADLE_PROJECT_adoArtifactsPassword env var maps onto the property automatically. Service repos don't include the init script — it lives in the gradle-jdk21 base image owned by platform.
// init.gradle (in gradle-jdk21 image)
allprojects {
repositories {
maven {
url 'https://pkgs.dev.azure.com/LithiaMotors/.../_packaging/maven-feed/maven/v1'
credentials {
username = adoArtifactsUsername ?: 'dummy'
password = adoArtifactsPassword ?: ''
}
}
}
}
Maven equivalent
For Maven services, ~/.m2/settings.xml declares a <server> whose <password> is ${env.ADO_ARTIFACTS_TOKEN} and a <repository> pointing at the same feed URL. The base image bakes settings.xml; the pipeline injects the token via env var. Same model, different tool.
Build the creds resolver + leak check
Write buildGradleAuth({ feedUrl, token }) returning an init.gradle snippet that uses project properties (NOT inline interpolation of the token). Write leakCheck(dockerfile) that scans a Dockerfile for RUN echo $ado_artifacts_token or hardcoded ADO_ARTIFACTS_TOKEN= — return { ok, leaks }. Log a clean Dockerfile and a leaky one.
Prefer BuildKit secret mounts in new code
docker build --secret id=adotoken,env=ADO_ARTIFACTS_TOKEN . + RUN --mount=type=secret,id=adotoken … keeps the value out of build history entirely. Lythia is migrating; if you're writing a new Dockerfile, use this from day one.
Quiz: System.AccessToken vs PAT
Q: Should I create a PAT for the build to use? A: No. System.AccessToken is auto-issued per pipeline run, scoped to the build identity, expires when the run ends. PATs are long-lived and tend to leak into ~/.gradle, .bash_history, screenshots. Use System.AccessToken for everything that can.