Broken while-loop on evaluation of azure commands in Bash Script

Viewed 75

I have a csv file, params.csv, consisting of the following dummy data for instance:

"Victor Okwuemu","n939NekyPa8910","VictorO003_gmail.com#EXT#@Hegemongroup.onmicrosoft.com"
"Jackson Kiraly","n939Nek00O1yPa","JacksonK004_gmail.com#EXT#@Hegemongroup.onmicrosoft.com"
"Benson Thomas","n939Nek025yPa","bensonT005_gmail.com#EXT#@Hegemongroup.onmicrosoft.com"

I used a while loop to read each line and extract the strings in each line based on the comma into the variables: displayname, password and upn using these commands: $(echo $line | cut -d "," -f 1), $(echo $line | cut -d "," -f 2) and $(echo $line | cut -d "," -f 3) respectively as shown below:

file='params.csv'
while read line; do
  displayName=$(echo $line | cut -d "," -f 1)
  password=$(echo $line | cut -d "," -f 2)
  upn=$(echo $line | cut -d "," -f 3)
  var1="az ad user create --display-name"
  var2="--password"
  var3="--user-principal-name"
  eval ${var1} ${displayName} ${var2} ${password} ${var3} ${upn}
done < $file

My goal is to pass the extracted strings into an Azure Active Directory CLI commands to create multiple users in Azure Active Directory tenant without using Azure portal.

The program loops, evaluates the first line and adds the user to my Azure active directory tenant, then fails to do for other lines in the the file. However, when I comment out the 'eval' line and echo the variables, the while loop goes through every line and and extracts all the strings for each line.

I have tried writing the commands in various other format as follows:

az ad user create --display-name "$displayName" --password "$password" --user-principal-name "$upn"
echo "az ad user create --display-name $displayName --password $password --user-principal-name $upn"
az ad user create --display-name $(echo $line | cut -d "," -f 1 | tr -d '"') --password $(echo $line | cut -d "," -f 2 | tr -d '"') --user-principal-name $(echo $line | cut -d "," -f 3 | tr -d '"')

All of these methods have failed to create the users as I expected. Any body with an idea on this? The problem here is that the while loop breaks after evaluating the first line in the file.

1 Answers

As we should avoid using eval as much as possible due to its vulnerability, would you please try instead:

#!/bin/bash

file="params.csv"
cmd=("az" "ad" "user" "create" "--display-name")
var2="--password"
var3="--user-principal-name"

while IFS=, read -r displayName password upn; do
    "${cmd[@]}" "$displayName" "$var2" "$password" "$var3" "$upn"
done < <(tr -d '"' < "$file")
  • You don't need to repead cut command to split the lines. read builtin does it for you by assigning IFS to the delimiting character.
  • The command line arguments can be put in an array, not in the single string.
  • The surrounding double quotes are removed by the tr command, assuming double quotes are not embedded within the strings.

Btw your 1st trial az ad user create --display-name "$displayName" .. fails because the variables keeps the surrounding double quotes then the command line is equivalent to something like az ad user create --display-name "\"Victor Okwuemu\"" ...

As a side note, the echo command is often used to validate the command line while debugging, but please keep in mind that echo sometimes deceives us especially about the quotings. Even if the output of echo looks good, it does not mean the arguments are correctly passed to the command. An alternative is to write a test command such as:

#!/bin/bash
printf "%s\n" "$@"

which prints the passed arguments line by line.
Try to replace the command az with ./testcommand above, run, and examine the output whether the arguments are properly passed.

[Edit]
As a part of debugging, would you execute a script with the following commands:

az ad user create --display-name "Victor Okwuemu" --password "n939NekyPa8910" --user-principal-name "VictorO003_gmail.com#EXT#@Hegemongroup.onmicrosoft.com"
az ad user create --display-name "Jackson Kiraly" --password "n939Nek00O1yPa" --user-principal-name "JacksonK004_gmail.com#EXT#@Hegemongroup.onmicrosoft.com"
az ad user create --display-name "Benson Thomas" --password "n939Nek025yPa" --user-principal-name "bensonT005_gmail.com#EXT#@Hegemongroup.onmicrosoft.com"

[Edit2]
Would you please try the following for the test purpose:

#!/bin/bash

cmd=("az" "ad" "user" "create" "--display-name")
var2="--password"
var3="--user-principal-name"

while IFS=, read -r displayName password upn; do
    "${cmd[@]}" "$displayName" "$var2" "$password" "$var3" "$upn"
done < <(tr -d '"' <<EOS
"Victor Okwuemu","n939NekyPa8910","VictorO003_gmail.com#EXT#@Hegemongroup.onmicrosoft.com"
"Jackson Kiraly","n939Nek00O1yPa","JacksonK004_gmail.com#EXT#@Hegemongroup.onmicrosoft.com"
"Benson Thomas","n939Nek025yPa","bensonT005_gmail.com#EXT#@Hegemongroup.onmicrosoft.com"
EOS
)

[Edit3]
Would you please try the following script to generate another script newscript.sh which should be identical to the script in [Edit]:

#!/bin/bash

file="params.csv"
cmd=("az" "ad" "user" "create" "--display-name")
var2="--password"
var3="--user-principal-name"

{
    echo "#!/bin/bash"
    echo
    while IFS=, read -r displayName password upn; do
        echo "${cmd[@]}" "\"$displayName\"" "$var2" "\"$password\"" "$var3" "\"$upn\""
    done < <(tr -d '"' < "$file")
} > newscript.sh

Then run the generated script.

[Edit4]
Here is a variant of [Edit3] without generating a separate script:

#!/bin/bash

file="params.csv"
cmd=("./az" "ad" "user" "create" "--display-name")
var2="--password"
var3="--user-principal-name"

while IFS=, read -r displayName password upn; do
    echo "${cmd[@]}" "\"$displayName\"" "$var2" "\"$password\"" "$var3" "\"$upn\""
done < <(tr -d '"' < "$file") | bash -
  • The final pipeline | bash - feeds the output of previous processings (or generated az command lines) directly to bash instead of generating a script file.
  • The double quotes in the command line may be tricky. The outermost double quotes for each variable are removed by echo and the inner ones (escaped with backslashes) are removed when passed to the az command. Try to replace the command az with ./testcommand and see the output as I have noted in the first answer.
Related