Skip to content

Commit db2c6ad

Browse files
committed
Improve token handling and cleanup in docker.md for Linux/Bash script
Refactor token loading and cleanup functions in docker for Linux/Bash 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 db2c6ad

1 file changed

Lines changed: 100 additions & 26 deletions

File tree

docs/pipelines/agents/docker.md

Lines changed: 100 additions & 26 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}" || true
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
372-
# Let the agent ignore the token env variables
373-
export VSO_AGENT_IGNORE="AZP_TOKEN,AZP_TOKEN_FILE"
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+
446+
# Let the agent ignore sensitive env variables, including tokens and the client secret.
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)