Search code examples
amazon-web-servicesshellsalt-project

execution of salt cmd.run has different behaviors if it's CLI vs through state files. what's the difference?


I'm trying to set a variable TOKEN with a value of a IMDSv2 (AWS instance metadata) token to get an instance IP address.

Executing a set of commands through the salt CLI yields desired results: Here's the command below:

salt-ssh 'name.of.host' cmd.run cmd='TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -s -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") && curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/meta-data/public-ipv4'

However, I'm trying to execute the same command through an evaluated expression like so:

{{ salt["cmd.run"](cmd='TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -s -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") && curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/meta-data/public-ipv4')}}

and the following error shows up:

 FileNotFoundError: [Errno 2] No such file or directory: 'TOKEN=$(curl': 'TOKEN=$(curl'

My thinking is that the way salt evaluates command expressions through CLI is different from a jinja esque evaluation. I also understand that spaces are a factor in shell expressions for salt. I tried different combinations of putting escape characters behind the equal, dollar and parenthesis, but no such luck. I still think it has to do with special characters, but can't pinpoint what.

Any thoughts? Much appreciated.


Solution

  • As per the documentation, the cmd.run does not process commands through Shell by default. Quoting a "Warning" from the above link:

    This function does not process commands through a shell unless the python_shell flag is set to True. This means that any shell-specific functionality such as 'echo' or the use of pipes, redirection or &&, should either be migrated to cmd.shell or have the python_shell=True flag set here.

    So there are two options:

    • Instead of using cmd.run use cmd.shell. E.g.:

      {{ salt.cmd.shell('TOKEN=$(curl -X PUT ...)') }}
      
    • Or add python_shell=True to cmd.run. E.g.:

       {{ salt.cmd.run('TOKEN=$(curl -X PUT ...)', python_shell=True) }}