Unable to run shell script in Terraform - bash

I am trying to run a shell script with the local-exec command in Terraform. When I run this, it keeps coming up with the error "Can't open appsettings.sh". This script runs fine when run manually. Any ideas what I am missing?
resource "null_resource" "sp" {
triggers = {
shell_hash = "${sha256(file("${path.module}/appsettings.sh"))}"
}
provisioner "local-exec" {
command = "appsettings.sh ${azuread_application.rbac-server-principal.application_id} ${azuread_application.rbac-client-principal.application_id}"
interpreter = ["sh"]
working_dir = "${path.module}"
}
}
error message:
Error: Error running command 'appsettings.sh 59942507-xxxx-xxxx-xxxxx 4c64-xxxx-xxxx-xxxxx': exit status 127. Output: sh: 0: Can't open appsettings.sh 59942507-xxxx-xxxx-xxxxx 4c64-xxxx-xxxx-xxxxx'

The issue here is, terraform run the command as
["/bin/sh" "-c" "appsettings.sh arg1 arg2"]
So the command interpreter take appsettings.sh as the command name which is very similar to running as below
$appsettings.sh arg1 arg2
which cannot be done as there is no command like this. So to run that shell script you either has to provide the absolute path of the shell script or relative as shown below
$ ./appsettings.h
$ /home/user/appsettings.sh # Example
To achieve this rearrange your command attr as below
command = "./appsettings.sh ${azuread_application.rbac-server-principal.application_id} ${azuread_application.rbac-client-principal.application_id}"
OR
command = "${path.module}/appsettings.sh ${azuread_application.rbac-server-principal.application_id} ${azuread_application.rbac-client-principal.application_id}"
i.e. add ./appsettings

The error message is stating that it "Can't open" the script file.
The reason might be:
The script file is not present (may be it is not yet created on machine)
Permission issue
If you are running command through a program (e.g terraform or script) you need to specify the shell and or proper path.
Example:
/bin/sh /path/to/script/myscript.sh
or
/bin/bash /path/to/script/myscript.sh
I am not sure but I think local-exec is used to run the local commands like ls, echo, mkdir etc and remote-exec is used to run the script.
Though I have not yet used this provisioner but you can play around this.
Refrances:
https://www.terraform.io/docs/provisioners/local-exec.html
https://www.terraform.io/docs/provisioners/remote-exec.html

OK I get it now.
TLDR version
Remove interpreter (interpreter = ["sh"]) at all or add -c as another parameter, like this: interpreter = ["sh", "-c"] or even better interpreter = ["/bin/sh", "-c"]. Additionally you need to provide path to script so add ./ or ${path.module}/.
NOTE: also as far as I see script needs to be executable (chmod +x appsettings.sh).
Full version
The problem is with default interpretation of local-exec. If you do not set interpreter then the default one is /bin/sh -c (Linux) and if you add command/script with parameters/arguments it looks like this
["/bin/sh", "-c", "./appsettings.sh a b"]
but what you were trying to do is to run something like this
["/bin/sh", "./appsettings.sh a b"]
If we test both this approaches in terminal then we will see:
$ /bin/sh -c "./appsettings.sh a b"
Params: a b
$ /bin/sh "./appsettings.sh a b"
/bin/sh: 0: Can't open ./appsettings.sh a b
In /bin/sh and /bin/bash shells -c parameters is responsible for:
Read commands from the command_string operand instead of from the standard input.
NOTE: if you want to read more check this out: https://askubuntu.com/questions/831847/what-is-the-sh-c-command
Final corrected script should looks like this:
resource "null_resource" "sp" {
triggers = {
shell_hash = "${sha256(file("${path.module}/appsettings.sh"))}"
}
provisioner "local-exec" {
command = "./appsettings.sh ${azuread_application.rbac-server-principal.application_id} ${azuread_application.rbac-client-principal.application_id}"
}
}
If you would like to use bash instead of sh then it should be like this:
resource "null_resource" "sp" {
triggers = {
shell_hash = "${sha256(file("${path.module}/appsettings.sh"))}"
}
provisioner "local-exec" {
command = "./appsettings.sh ${azuread_application.rbac-server-principal.application_id} ${azuread_application.rbac-client-principal.application_id}"
interpreter = ["/bin/bash", "-c"]
}
}```

Related

Is it possible to run commandLine in gradle with bash -c?

I have a task in a build.gradle file, in which I'd like to run this command:
(export ENV_VAR=/my/path ; /path/to/build.sh)
I tried running this in gradle:
task myTask {
doLast {
exec {
commandLine ['bash', '-c', '"(export ENV_VAR=/my/path ; /path/to/build.sh)"']
}
}
}
Unfortunately, I have an error that says
Successfully started process 'command 'bash''
bash: (export ENV_VAR=/my/path ; /path/to/build.sh): No such file or directory
Now I'm sure the file exists and the specified paths are correct. Running this command manually in the terminal works.
Is there something in gradle that makes a bash -c like this not work? I can't think of another way to make an export like this otherwise.
Try without the extra quotes:
commandLine ['bash', '-c', '(export ENV_VAR=/my/path ; /path/to/build.sh)']
When you run that in the command line, your shell needs the quotes to pass to the command (which happens to be bash) as a single argument, but gradle is already doing that with that syntax, so bash is receiving literally one argument "(export ENV_VAR=/my/path ; /path/to/build.sh)" and since it does not recognize this as internal syntax, tries to run a command with this name.

Sbt external process can't process `eval` command

Running in sbt command "eval $(minikube docker-env)" !! log give exception.
[error] java.io.IOException: Cannot run program "eval": error=2, No such file or directory
but same command in bash script
#!/usr/bin/env bash
eval $(minikube docker-env)
Runn as "eval.sh" !! log
Work fine.
I can't understand why. Please explain.
eval is a shell feature. There is no way to call it from java to set up the environment for future commands the way you can for a shell.
If you want to run a second command from Java that depends on doing eval "$(minikube docker-env)" first, you can instead run a single shell with both commands:
String shellCommand = "eval \"$(minikube docker-env)\"; your-second-command";
Runtime.exec(new String[] { "sh", "-c", shellCommand });

Script not working as Command line

i've created simple bash script that do the following
:
#!/usr/bin/env bash
cf ssh "$1"
When I run the command line from the CLI like cf ssh myapp its running as expected, but when I run the script like
. myscript.sh myapp
I got error: App not found
I dont understand what is the difference, I've provided the app name after I invoke the script , what could be missing here ?
update
when I run the script with the following its working, any idea why the "$1" is not working ...
#!/usr/bin/env bash
cf ssh myapp
When you do this:
. myscript.sh myapp
You don't run the script, but you source the file named in the first argument. Sourcing means reading the file, so it's as if the lines in the file were typed on the command line. In your case what happens is this:
myscript.sh is treates as the file to source and the myapp argument is ignored.
This line is treated as a comment and skipped.
#!/usr/bin/env bash
This line:
cf ssh "$1"
is read as it stands. "$1" takes the value of $1 in the calling shell. Possibly - most likely in your case - it's blank.
Now you should know why it works as expected when you source this version of your script:
#!/usr/bin/env bash
cf ssh myapp
There's no $1 to resolve, so everything goes smoothly.
To run the script and be able to pass arguments to it, you need to make the file executable and then execute it (as opposed to sourcing). You can execute the script for example this way:
./script.bash arg1 arg2

Jenkins Pipeline cannot execute SH command file in a Windows slave

I'm executing this code:
node('my_windows_slave') {
sh 'ls'
}
In my Windows slave I can properly execute sh command:
But the pipeline script can't run the .sh file:
[Pipeline] sh
[D:\workspace\sandbox_pipeline] Running shell script
sh: D:\workspace\sandbox_pipeline#tmp\durable-2d7dd2f8\script.sh: command not found
What I could notice is that this .sh file is not even created, once I tried with bat and worked fined.
Any clue what could be the problem?
[UPDATE]
Jenkins somehow can't create the SH temporary file. Already checked the log, permissions, everything that came to my mind.
I will leave my workaround as an answer for while before approve it once I'm still not 100% sure about the root cause and might someone else show up with a elegant solution...
def shell(command) {
return bat(returnStdout: true, script: "sh -x -c \"${command}\"").trim()
}
Attention
You still executing SH commands in a CMD, it means some %d for example can break your SH command.
Use the bat step instead of sh.
From Jenkins docs:
Windows-based systems should use the bat step for executing batch commands.

How can I retrieve the output of my command when using rake sh?

I am running a command using sh and need to read the output of that command. e.g.
sh "whoami"
But sh only seems to return true rather than a string containing the output of the whoami command. Does anyone know of a solution?
There are several ways:
output = `whoami`
#or
output = %x[whoami]
# or using 'system' but in case of errors it's gonna return false
output = system "whoami"
Just use backquotes to execute the statement:
output = `whoami`
The result will be in the 'output' variable.
i wasn't sure how to get those other methods to fail on error, so i went with redirection:
sh "mysql --verbose #{connection_options} < #{sql_file} > #{sql_file_output_file}" do |ok, status|
ok or fail "mysql file failed [#{sql_file}"
end

Resources