Skip to content

Commit 6e0e6d1

Browse files
committed
Improve token handling and cleanup in docker.md
Refactor token loading and cleanup functions in docker script. Fixes issue with this error during cleanup WRITE ERROR: VS30063: You are not authorized to access https://dev.azure.com. Also fixed issue with hard code AZP_TOKEN_FILE location being a hard coded path /azp/.token which may not exists. Changed it to use the same location as the script location.
1 parent 1fa9f5f commit 6e0e6d1

1 file changed

Lines changed: 91 additions & 25 deletions

File tree

docs/pipelines/agents/docker.md

Lines changed: 91 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -316,42 +316,96 @@ Next, create the Dockerfile.
316316
#!/bin/bash
317317
set -e
318318
319-
if [ -z "${AZP_URL}" ]; then
320-
echo 1>&2 "error: missing AZP_URL environment variable"
321-
exit 1
322-
fi
319+
# Load a token either from the environment variable or by using the service principal credentials.
320+
load_azp_token() {
321+
# Always un-export AZP_CLIENTSECRET so it is never inherited by
322+
# child processes (e.g., agent job processes). It remains available
323+
# as a shell variable so that it can be used to generate a token.
324+
export -n AZP_CLIENTSECRET
325+
326+
if [ -n "$AZP_CLIENTID" ]; then
327+
if [ -z "$AZP_CLIENTSECRET" ]; then
328+
echo 1>&2 "error: AZP_CLIENTSECRET must be set when AZP_CLIENTID is used"
329+
exit 1
330+
fi
331+
332+
if [ -z "$AZP_TENANTID" ]; then
333+
echo 1>&2 "error: AZP_TENANTID must be set when AZP_CLIENTID is used"
334+
exit 1
335+
fi
336+
337+
echo "Using service principal credentials to get token"
338+
# Isolate Azure CLI state to a dedicated, restrictive directory to avoid
339+
# leaking cached credentials/tokens to subsequent pipeline job processes.
340+
AZP_TOKEN="$(
341+
export AZURE_CONFIG_DIR="$(mktemp -d)"
342+
chmod 700 "$AZURE_CONFIG_DIR"
343+
az_cli_cleanup() {
344+
# Log out and remove the isolated Azure CLI config directory to purge cached tokens.
345+
az logout --username "$AZP_CLIENTID" >/dev/null 2>&1 || true
346+
az account clear >/dev/null 2>&1 || true
347+
rm -rf "$AZURE_CONFIG_DIR"
348+
}
349+
trap az_cli_cleanup EXIT
350+
az login --allow-no-subscriptions --service-principal --username "$AZP_CLIENTID" --password "$AZP_CLIENTSECRET" --tenant "$AZP_TENANTID" >/dev/null
351+
# adapted from https://learn.microsoft.com/en-us/azure/databricks/dev-tools/user-aad-token
352+
az account get-access-token --query accessToken --output tsv
353+
)"
354+
echo "Token retrieved"
355+
fi
323356
324-
if [ -n "$AZP_CLIENTID" ]; then
325-
echo "Using service principal credentials to get token"
326-
az login --allow-no-subscriptions --service-principal --username "$AZP_CLIENTID" --password "$AZP_CLIENTSECRET" --tenant "$AZP_TENANTID"
327-
# adapted from https://learn.microsoft.com/en-us/azure/databricks/dev-tools/user-aad-token
328-
AZP_TOKEN=$(az account get-access-token --query accessToken --output tsv)
329-
echo "Token retrieved"
330-
fi
357+
if [ -z "${AZP_TOKEN_FILE}" ]; then
358+
if [ -z "${AZP_TOKEN}" ]; then
359+
echo 1>&2 "error: missing AZP_TOKEN environment variable"
360+
exit 1
361+
fi
331362
332-
if [ -z "${AZP_TOKEN_FILE}" ]; then
333-
if [ -z "${AZP_TOKEN}" ]; then
334-
echo 1>&2 "error: missing AZP_TOKEN environment variable"
335-
exit 1
363+
AZP_TOKEN_FILE="$(dirname "$0")/.token"
336364
fi
337365
338-
AZP_TOKEN_FILE="/azp/.token"
339-
echo -n "${AZP_TOKEN}" > "${AZP_TOKEN_FILE}"
340-
fi
341-
342-
unset AZP_CLIENTSECRET
343-
unset AZP_TOKEN
366+
if [ -n "${AZP_TOKEN-}" ]; then
367+
if [ -d "${AZP_TOKEN_FILE}" ]; then
368+
echo 1>&2 "error: AZP_TOKEN_FILE is a directory, expected a file path: ${AZP_TOKEN_FILE}"
369+
exit 1
370+
fi
371+
# Ensure the parent directory exists and the target path is writable.
372+
if [ ! -w "${AZP_TOKEN_FILE}" ] && [ ! -w "$(dirname "${AZP_TOKEN_FILE}")" ]; then
373+
echo 1>&2 "error: AZP_TOKEN_FILE is not writable (or its parent directory is missing/unwritable): ${AZP_TOKEN_FILE}"
374+
exit 1
375+
fi
376+
echo -n "${AZP_TOKEN}" > "${AZP_TOKEN_FILE}"
377+
else
378+
if [ ! -r "${AZP_TOKEN_FILE}" ]; then
379+
echo 1>&2 "error: AZP_TOKEN_FILE is not readable: ${AZP_TOKEN_FILE}"
380+
exit 1
381+
fi
382+
if [ ! -s "${AZP_TOKEN_FILE}" ]; then
383+
echo 1>&2 "error: AZP_TOKEN_FILE is empty: ${AZP_TOKEN_FILE}"
384+
exit 1
385+
fi
386+
fi
344387
345-
if [ -n "${AZP_WORK}" ]; then
346-
mkdir -p "${AZP_WORK}"
347-
fi
388+
# Unset the AZP_TOKEN environment variable to prevent it from being exposed.
389+
# At this point the token is only available in the file specified by AZP_TOKEN_FILE.
390+
unset AZP_TOKEN
391+
}
348392
393+
# Cleanup function to remove the agent configuration.
349394
cleanup() {
350395
trap "" EXIT
351396
352397
if [ -e ./config.sh ]; then
353398
print_header "Cleanup. Removing Azure Pipelines agent..."
354399
400+
# Try to get a new token if using service principal credentials, as the old one
401+
# might have expired between the time it was waiting to run a job and now.
402+
# If refresh fails, continue cleanup with the existing token file.
403+
if [ -n "$AZP_CLIENTID" ]; then
404+
if ! load_azp_token; then
405+
echo "Warning: failed to refresh Azure Pipelines token during cleanup. Continuing with the existing token."
406+
fi
407+
fi
408+
355409
# If the agent has some running jobs, the configuration removal process will fail.
356410
# So, give it some time to finish the job.
357411
while true; do
@@ -369,8 +423,20 @@ Next, create the Dockerfile.
369423
echo -e "\n${lightcyan}$1${nocolor}\n"
370424
}
371425
426+
if [ -z "${AZP_URL}" ]; then
427+
echo 1>&2 "error: missing AZP_URL environment variable"
428+
exit 1
429+
fi
430+
431+
# Load the AZP token for initial setup.
432+
load_azp_token
433+
434+
if [ -n "${AZP_WORK}" ]; then
435+
mkdir -p "${AZP_WORK}"
436+
fi
437+
372438
# Let the agent ignore the token env variables
373-
export VSO_AGENT_IGNORE="AZP_TOKEN,AZP_TOKEN_FILE"
439+
export VSO_AGENT_IGNORE="AZP_TOKEN,AZP_TOKEN_FILE,AZP_CLIENTSECRET"
374440
375441
print_header "1. Determining matching Azure Pipelines agent..."
376442

0 commit comments

Comments
 (0)