Skip to content

Commit 56f81ea

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 56f81ea

1 file changed

Lines changed: 99 additions & 25 deletions

File tree

docs/pipelines/agents/docker.md

Lines changed: 99 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -316,42 +316,104 @@ 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+
# If the file already exists, it must itself be writable.
372+
if [ -e "${AZP_TOKEN_FILE}" ]; then
373+
if [ ! -w "${AZP_TOKEN_FILE}" ]; then
374+
echo 1>&2 "error: existing AZP_TOKEN_FILE is not writable: ${AZP_TOKEN_FILE}"
375+
exit 1
376+
fi
377+
else
378+
if [ ! -w "$(dirname "${AZP_TOKEN_FILE}")" ]; then
379+
echo 1>&2 "error: AZP_TOKEN_FILE parent directory is missing or not writable: $(dirname "${AZP_TOKEN_FILE}")"
380+
exit 1
381+
fi
382+
fi
383+
(umask 177 && echo -n "${AZP_TOKEN}" > "${AZP_TOKEN_FILE}")
384+
chmod 600 "${AZP_TOKEN_FILE}"
385+
else
386+
if [ ! -r "${AZP_TOKEN_FILE}" ]; then
387+
echo 1>&2 "error: AZP_TOKEN_FILE is not readable: ${AZP_TOKEN_FILE}"
388+
exit 1
389+
fi
390+
if [ ! -s "${AZP_TOKEN_FILE}" ]; then
391+
echo 1>&2 "error: AZP_TOKEN_FILE is empty: ${AZP_TOKEN_FILE}"
392+
exit 1
393+
fi
394+
fi
344395
345-
if [ -n "${AZP_WORK}" ]; then
346-
mkdir -p "${AZP_WORK}"
347-
fi
396+
# Unset the AZP_TOKEN environment variable to prevent it from being exposed.
397+
# At this point the token is only available in the file specified by AZP_TOKEN_FILE.
398+
unset AZP_TOKEN
399+
}
348400
401+
# Cleanup function to remove the agent configuration.
349402
cleanup() {
350403
trap "" EXIT
351404
352405
if [ -e ./config.sh ]; then
353406
print_header "Cleanup. Removing Azure Pipelines agent..."
354407
408+
# Try to get a new token if using service principal credentials, as the old one
409+
# might have expired between the time it was waiting to run a job and now.
410+
# If refresh fails, continue cleanup with the existing token file.
411+
if [ -n "$AZP_CLIENTID" ]; then
412+
if ! ( load_azp_token ); then
413+
echo "Warning: failed to refresh Azure Pipelines token during cleanup. Continuing with the existing token."
414+
fi
415+
fi
416+
355417
# If the agent has some running jobs, the configuration removal process will fail.
356418
# So, give it some time to finish the job.
357419
while true; do
@@ -369,8 +431,20 @@ Next, create the Dockerfile.
369431
echo -e "\n${lightcyan}$1${nocolor}\n"
370432
}
371433
434+
if [ -z "${AZP_URL}" ]; then
435+
echo 1>&2 "error: missing AZP_URL environment variable"
436+
exit 1
437+
fi
438+
439+
# Load the AZP token for initial setup.
440+
load_azp_token
441+
442+
if [ -n "${AZP_WORK}" ]; then
443+
mkdir -p "${AZP_WORK}"
444+
fi
445+
372446
# Let the agent ignore the token env variables
373-
export VSO_AGENT_IGNORE="AZP_TOKEN,AZP_TOKEN_FILE"
447+
export VSO_AGENT_IGNORE="AZP_TOKEN,AZP_TOKEN_FILE,AZP_CLIENTSECRET"
374448
375449
print_header "1. Determining matching Azure Pipelines agent..."
376450

0 commit comments

Comments
 (0)