Hi @bingerk!
Unfortunately on Windows the handling of command line quoting and escaping is quite tricky, because each application is responsible for handling its parsing itself and so each application can potentially use different rules for interpreting the given string of arguments.
As a consequence, Terraform follows the following sequence of steps in order to execute your given command:
-
It first takes your string and produces a command line argument array representing the command line:
cmd /C "az sql server ad-admin create -g 'my-rg' -s 'my-sql' -u 'jon doe' -i 'acar5515-9555-4f3c-8df5-ed55555c55'"
-
To launch that command, Terraform uses the Windows CreateProcess
API, passing that constructed command line as follows:
CreateProcess("cmd", "/C \"az sql server ad-admin create -g 'my-rg' -s 'my-sql' -u 'jon doe' -i 'acar5515-9555-4f3c-8df5-ed55555c55'\"", ...)
-
The Windows command interpreter cmd.exe
then gets to interpret that second string argument in whatever way it wants. I don’t know the internals of the command interpreter, but I believe it takes the quoted string given after /C
and treats it in a similar way to if you’d typed that string at the Windows command prompt, which includes searching for special sequences like I/O redirection with >foo
, etc. It’s presumably then calling CreateProcess
itself, something like this:
CreateProcess("az", "sql server ad-admin create -g 'my-rg' -s 'my-sql' -u 'jon doe' -i 'acar5515-9555-4f3c-8df5-ed55555c55'", ...)
-
At that point, it’s up to this az
command to decide what to do with that string. It may or may not support using '
as a quoting character. If it’s doing its command line parsing using the C library argument parser or the CommandLineToArgvW
API function – both of which are common choices – then it would not support '
as a quoting character and would require you to use "
instead.
With all of those details aside, what I’d try next is to use "
instead of '
as the quoting character, which would therefore be supported by a program parsing the command line in the standard way on Windows. Unfortunately that does require some escaping in Terraform:
command = "az sql server ad-admin create -g \"my-rg\" -s \"my-sql\" -u \"jon doe\" -i \"acar5515-9555-4f3c-8df5-ed55555c55\""
If this fixes it, then of course it will raise the question of why this was working for you when you ran it directly from the Windows command prompt. My best guess for that would be that you were typing the command into PowerShell rather than into cmd.exe
, and so PowerShell was doing its own pre-processing of the arguments before passing them to CreateProcess
internally.
If I recall correctly, PowerShell follows the following procedure for launching executables (as opposed to its own cmdlets):
-
Parse the command line into a sequence of strings using PowerShell’s own quoting rules, which do support '
as a quoting character and would thus produce a sequence like this from your input:
["az", "sql", "server", "ad-admin", "create", "-g", "my-rg", "-s", "my-sql", "-u", "jon doe", "-i", "acar5515-9555-4f3c-8df5-ed55555c55"]
-
In order to use this with CreateProcess
it must turn everything except the first argument into a single string, which it does by joining them all with spaces and adding quotes around any item that already has a space in it:
CreateProcess("az", "sql server ad-admin create -g my-rg -s my-sql -u \"jon doe\" -i acar5515-9555-4f3c-8df5-ed55555c55")
-
This time, the command line string received by the az
program would be the following:
sql server ad-admin create -g my-rg -s my-sql -u "jon doe" -i acar5515-9555-4f3c-8df5-ed55555c55
…and so jon doe
is now in double quotes as the standard argument parser expects, allowing it to work.
If the above explains the difference, then another option available to you is to ask Terraform to run the command using powershell.exe
instead of cmd.exe
. I’m not sure exactly how that would be done because I’ve never really used Powershell, but based on the powershell.exe
documentation I would expect something like this to work:
provisioner "local-exec" {
interpreter = ["powershell.exe", "-Command"]
command = "az sql server ad-admin create -g 'my-rg' -s 'my-sql' -u 'jon doe' -i 'acar5515-9555-4f3c-8df5-ed55555c55'"
}
The interpreter
argument overrides Terraform’s default of using cmd /C "..."
to run the command you provided, making Terraform use powershell.exe -Command "..."
instead.
I’m sorry there’s so much complexity here! The design of command line argument handling on Windows prevents there from being a straightforward answer to this question, but hopefully the above gives you some new things to try and some ideas as to why things seem to be behaving differently at the command line directly vs. in Terraform.
This sort of complexity is why Terraform provisioners are a last resort; if there’s any way to do that ad-admin create
operation using a real resource type in the azurerm
provider then that’d make this a lot less messy to achieve.
I don’t know what that command does, so I don’t know if any such resource type exists, but if not then the Azure provider team might be open to adding it if it’s calling into a normal Azure API underneath.