@@ -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