Best way to put a string into a file (via a module) without interpolation of any kind

i’m trying to make use of a module which sets up a service and as part of that it takes in a string variable which defines a short bash script to run. The script i’ve got isn’t hugely long but it’s proving to be quite a pain to escape properly for terraform to pass it properly.

if keys=$(printenv | egrep -o \"GITLAB_DEPLOY_KEY[^=]*\") && which ssh-agent; then set -o pipefail; eval \"$(ssh-agent)\" && for key in ${keys}; do echo Loading ${key} && printenv ${key} | tr -d \\\r | ssh-add -; done; fi || true;

Is there an easy way of dealing with this situation ? Some way i can tell terraform not to interpolate this ? I need the above put directly into my file on disk as-is, including the $(), ${}, and all. I messed about with base64encode() but i didnt get it working fully, it still extracted the eval() when putting it into the file, but there might be an option there maybe ? I’ve tried doing the escape sequence dance but some parts, particularly the \\\r are causing issues there

Hi @siology,

If you put the source content for this script in a file alongside your .tf configuration files then you can load its content into memory using the file function, which will do no processing of the content except to decode the bytes as UTF-8 to create a Terraform string.

If you use path.module as part of the path argument, as shown in the example in the docs, then Terraform will read the file from the same directory as the .tf file which requested it.

i tried that, but the eval() was evaluated when it was put into the file, so i had the SSH_AGENT= stuff in the string once it was at the destination.

Hi @siology,

Terraform’s template syntax doesn’t include anything that would match eval \"$(ssh-agent)\" as anything other than a literal string, so if you are seeing errors about that part then that would suggest to me that the parsing/evaluation of that part is happening at some other layer than Terraform’s template parser, such as inside the shell interpreter itself.

The only parts of the string you shared that would be recognized by Terraform as template syntax are the two ${key} sequences and the ${keys} sequence, which Terraform’s template syntax would understand as template interpolation.

However, the file function doesn’t do any template interpolation of the file it loaded, so even those sequences will not be interpreted by Terraform in that case.

In order to better understand what’s happening, it would help if you could share more of your configuration and ideally also the output you’re seeing from Terraform when you try to apply it.

I tried to reformat what you shared so I could read it more easily. I’ve included it below just in case it’s useful for others who might also refer to this question and have ideas about what might be going on.

if keys=$(printenv | egrep -o \"GITLAB_DEPLOY_KEY[^=]*\") && which ssh-agent; then
   set -o pipefail
   eval \"$(ssh-agent)\" && for key in ${keys}; do
     echo Loading ${key} && printenv ${key} | tr -d \\\r | ssh-add -
fi || true

Looking again at my reformatting of your shell script, I notice the following part which seems a bit suspect to me:

eval \"$(ssh-agent)\"

You’ve escaped the quotes here and so you’re asking the shell to evaluate a string that is wrapped in literal quotes. I would therefore expect the result to be as if you’d just written a quoted string directly in the shell script.

I imagine your goal here was to ask the shell to execute the output from ssh-agent as if it were more shell code… it presumably contains some variable declarations, as is typical for that command. In that case, I’d expect that to use unescaped quotes, because you’d then be asking the shell to evaluate directly the output of that command, rather than evaluating it surrounded by literal quotes:

eval "$(ssh-agent)"

Your other escaped quotes are perhaps similarly problematic, but I focused on this one just because it’s the one you mentioned in your previous message.

A nice benefit of putting the script into a separate file and loading it with the file function is that you’ll have a plain shell script on disk which you can try executing directly outside of Terraform in order to test it. I would suggest making sure the script does what you intend it to do outside of Terraform first, and then only after that worry about making Terraform read in the file and make use of it.