0

I have this command which I want to variabilize. Particularly I want the latest argument presence to depend on an environment variable.

ansible-playbook --inventory inventories/ssg-dev deploy.yml --vault-id dev@~/ansible-password --skip-tags "clear-data"

Here is my attempt:

ansible-playbook --inventory inventories/ssg-$ENVIRONMENT deploy.yml --vault-id $ENVIRONMENT@~/ansible-password $( [ $DATA_SCHEMAS_MIGRATION == true ] && echo ' --skip-tags "clear-data"')

When I set my environment variables and run that command with an echo before to check the output, it is the exact same as my original command. However, the argument doesn't seem to be passed when I run it, which makes me believe that the environment variable is not evaluated in the command substitution.

Why my code doesn't work and how to fix it?

Guerric P
  • 103
  • 4

2 Answers2

2

The double quotes around clear-data are not evaluated because of the single ones. It results in the argument being "clear-data" and not clear-data as you would expect.

Try this instead:

ansible-playbook --inventory "inventories/ssg-$ENVIRONMENT" deploy.yml --vault-id "$ENVIRONMENT@~/ansible-password" $([ "$DATA_SCHEMAS_MIGRATION" = true ] && echo '--skip-tags clear-data')

Note that I also added double quotes where you are using $ENVIRONMENT and $DATA_SCHEMAS_MIGRATION to prevent globbing and word splitting.

ShellCode
  • 225
  • It works without the quotes surrounding clear-data but I don't understand why... – Guerric P Mar 06 '23 at 20:12
  • My original command works with double quotes, my attempt generates the exact same command, yet I have to remove it? If you have an explanation to this, I'd be glad to know. – Guerric P Mar 06 '23 at 20:14
  • Because ansible doesn't know "clear-data" it knows clear-data however. Try this to understand: create a folder named test, then try to run ls 'test' and ls '"test"' do you get it now ? – ShellCode Mar 06 '23 at 20:14
  • Yes I think I got it, thanks – Guerric P Mar 06 '23 at 20:21
1

It's best to use an array when constructing a command line from variables. It eliminates almost all of the word-splitting problems and other hassles related to using & combining single-, double-, and un-quoted variables that you would encounter trying to use scalar variables.

Using an array also results in code that is much clearer, more readable, and easier to modify (instead of trying to modify a long and complicated line with lots of args and conditionals on it, you can separate that into multiple lines, each adding one or more args)

Just build up an array containing all the args you want to use with a command (you can do this all at once or additively with +=, even using conditionals and/or loops as needed), and then execute your command with all elements of the array as an argument.

For example:

ap_args=(--inventory "inventories/ssg-$ENVIRONMENT" deploy.yml)
ap_args+=(--vault-id "$ENVIRONMENT@~/ansible-password")
[ "$DATA_SCHEMAS_MIGRATION" == true ] && ap_args+=(--skip-tags clear-data)

ansible-playbook "${ap_args[@]}"

Also, remember to quote your variables. See Why does my shell script choke on whitespace or other special characters? and $VAR vs ${VAR} and to quote or not to quote

See also How can we run a command stored in a variable?

Note: arrays require bash, ksh, zsh, or some other shell that supports them. POSIX sh (e.g. ash/dash) does not.

cas
  • 78,579