Terraform - Unable to run multiple commands in local exec
I think there are two separate things going on here which are actually not related.
The main problem here is in how you've written your local-exec
script:
command = <<EOT
"chmod +x install-istio.sh"
"./install-istio.sh"
EOT
This will become the following shell script to run:
"chmod +x install-istio.sh"
"./install-istio.sh"
By putting the first command line in quotes, you're telling the shell to try to run a program that is called chmod +x install-istio.sh
without any arguments. That is, the shell will try to find an executable in your PATH
called chmod +x install-istio.sh
, rather than trying to run a command called just chmod
with some arguments as I think you intended.
Remove the quotes around the command lines to make this work. Quotes aren't needed here because neither of these commands contain any special characters that would require quoting:
command = <<-EOT
chmod +x install-istio.sh
./install-istio.sh
EOT
The warning message about interpolation-only expressions is unrelated to the problem of running these commands. This is telling you that you've used a legacy syntax that is still supported for backward compatibility but no longer recommended.
If you are using the latest version of Terraform at the time of writing (one of the v0.15 releases, or later) then you may be able to resolve this and other warnings like it by switching into this module directory and running terraform fmt
, which is a command that updates your configuration to match the expected style conventions.
Alternatively, you could manually change what that command would update automatically, which is to remove the redundant interpolation markers around path.module
:
working_dir = path.module
Local-exec using powershell not executing commands
I tested the same with something like below :
provider "azurerm" {
features{}
}
data "azurerm_resource_group" "example"{
name = "ansumantest"
}
variable "function_apps" {
default = ["ansumantestfunc1","ansumantestfunc2"]
}
variable "Subscription" {
default = "948d4068-xxxx-xxxx-xxxx-xxxxxxxxxxx"
}
resource "null_resource" "example2" {
count = length(var.function_apps)
provisioner "local-exec" {
command = <<Settings
$ResourceGroupName = "${data.azurerm_resource_group.example.name}"
$FunctionAppName = "${var.function_apps[count.index]}"
$SubscriptionId = "${var.Subscription}"
Get-AzFunctionApp -ResourceGroupName $ResourceGroupName -Name $FunctionAppName -SubscriptionId $SubscriptionId
Settings
interpreter = ["PowerShell", "-Command"]
}
}
Output:
Note :
I am using Terraform v1.1.0 on windows_amd64
- provider registry.terraform.io/hashicorp/azurerm v2.90.0
- provider registry.terraform.io/hashicorp/null v3.1.0
Terraform local-exec to pass new resource( not hard-coding)
Use for_each to execute the batch script once per resource in the resource_list variable
resource "null_resource" "mytest" {
for_each = toset(var.resource_list)
# using timestamp() will cause the script to run again for every value on
# every run since the value changes every time.
# you probably don't want that behavior.
# triggers = {
# run = "${timestamp()}"
# }
provisioner "local-exec" {
command = "sh test.sh ${each.value}"
}
}
When you run this the first time it will execute the script once for each of the resources in resource_list
. Then the terraform state file will be updated to know what has already run, so any subsequent runs will only include new items added to resource_list
.
How to write Windows-compatible command lines in Terraform local-exec provisioner
The procedure and conventions for quoting and escaping on the command line are quite different between Unix and Windows.
On Unix, quoting and escaping is a concern of the shell itself, and Unix shells typically understand the contents of single quotes '
as totally verbatim text, while the contents of "
can contain some interpolation metacharacters but spaces are still taken literally, rather than separators. The shell handles the tokenization into a set of arguments and passes those arguments to the program as an array of strings.
On Windows, quoting and escaping is the concern of the program being run1, and not of the command interpreter cmd.exe
. The command line you type will (more or less) be passed exactly as you wrote it to the program you're running -- curl
in this case -- and it's left entirely up to that program to decide what symbols like "
and '
might mean and where to draw the boundaries between separate arguments.
In practice, most modern CLI applications on Windows are either built using Microsoft Visual C++ or with some other software that emulates its command line parsing conventions. If curl
is such a program (which is likely, since it is written in C) then unfortunately the relevant rules are quite complicated, but for your purposes here we can reduce it to a few key facts:
- The single quote character
'
is taken literally. Unlike on Unix, it does not represent verbatim text. - You can use the double quote character
"
to represent a sequence where spaces should be taken literally rather than used as argument separators.
Therefore in order to write a command line that is portable across both Unix and Windows, you will need to use the double quote character "
and ensure that the strings you interpolate within the quotes don't contain any characters that would be interpreted as special by either set of parsing rules.
In your case, I think that would be the following as long as you can be sure that none of the variables this refers to will contain quote characters, $
characters, or anything else that a Unix shell might interpret as special inside double quotes:
command = <<-EOT
curl "https://${var.cognitive_search_name}.search.windows.net/indexes?api-version=2021-04-30-Preview" --header "Accept: application/json" --header "api-key: ${var.key}" --data "@${each.key}"
EOT
Writing portable command lines is quite challenging due to this significant architectural difference between Unix and Windows, which is one of the reasons why the Terraform documentation recommends considering provisioners as a last resort. If possible it would typically be better to use a specialized provider to make a request like this, but I'm not familiar enough with the API you're requesting here to know which provider would potentially offer it.
1 Technically, the command interpreter does do some parsing of its own to handle environment variable substitution like %%FOO%%
and the I/O redirection sequences like pipes and >
/<
markers.
The command interpreter therefore has its own separate escaping syntax which can potentially come into play if your command line ended up including any of the command interpreter's special characters.
Fortunately the command interpreter's parser also has some basic understanding of "
as a marker for turning off some of its special processing, but we still need to watch out for %%VARIABLE%%
sequences inside quotes, so you'll need to be sure that your variables to substitute don't contain anything like that.
Related Topics
Profiling Arbitrary Cuda Applications
Git - Crlf Issue in Windows + Linux Dual Boot
File/Directory Permissions Trailing + ( Drwxr-Xr-X+ )
Unix/Linux Ipc: Reading from a Pipe. How to Know Length of Data at Runtime
Reading Kernel Memory Using a Module
How to Cancel Command in Grunt Shell
Mmap Flag Map_Uninitialized Not Defined
Linux Device Driver Unsafe Fxsave/Fxrstor Bug - Any Precedents
Mpc/Mpd on Linux: How to Play Local Wav File
Add Timestamp to Cat Output from Shell Script
Do Here-Strings Undergo Word-Splitting
How to Get a Faster Output Pipe Than /Dev/Null
How to Set Errno in Linux Device Driver