-
Notifications
You must be signed in to change notification settings - Fork 50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
External Data Source With Multiple Parameters #27
Comments
Hi @luckerby, Unfortunately on Windows systems the handling of command line arguments can get pretty complex and hard to predict. 😖 The root problem is that on Windows the command line of a process is just a single string, rather than an array of strings as it is on Unix. This means that any additional layer the command line passes through will define its own tokenization which can affect what is seen by the layer afterwards. If I'm understanding correctly, your command line is being processed by the following layers:
I've not found any way to navigate these sequences of joining and splitting arguments except by trial and error. It might be helpful to eliminate the command interpreter layer altogether to remove one level of processing: data "external" "NetworkObtainedData" {
program = ["ExcelUpdateTool.exe", "\"regex\"", "machine_name", "available"]
} I wish I could give a more specific suggestion here, but command line parsing on Windows is always ultimately at the mercy of the program being run, and so the only way to truly know what is needed is to review the code of the programs in question and reverse-engineer how they process the command line. I would suggest avoiding PowerShell because its own command line parsing rules are quite complex themselves, and so it is quite complex to get exact control over quoting passed to external programs. The Windows command interpreter ( |
Thank you for the detailed reply, Martin. The flow especially is really nicely detailed. I’ve made some more tests on my side, and added the results below. TL;DR: Invoking the external tool you need, and specifying the parameters it requires directly in the terraform plan is the way to go. I’ve made the incorrect assumption that only 2 values are supported for the Long version: I’ve tested a few ways of passing various parameter values, with and without Leaving where I picked off in my original post, back then I didn’t include the command line that So, following the trail, and expanding Process Explorer's “Command line” fields we have the following.
Just as Martin correctly predicted, It looks like the shell itself The debug output for the tool itself lists the parameters sent across just as we’d have wanted them – note that the backslashes are gone: Let’s now take something more closer to real life – say a regex identifying all IP addresses starting with 192.168.4, which translates to
Through trial and error, I’ve discovered that the content of the (JSON) regex file itself has to contain double backslashes as below. When
What reaches the tool itself is the quoted regex, as per below. Within the tool, one would need to strip the quotes to get the original regex string. I’m suspecting terraform itself is parsing the value in the JSON file and stripping each backslash from each doubled set, since Go’s own EscapeArg doesn’t have a rule for this, and by the time the string reaches For the particular IP regex used above, it turns out that the quotes aren’t really necessary after all. So, stripping away the quotes from the external provider’s config:
Again, through trial and error, it turns out that once the quotes above are gone, the caret in the regex needs to be doubled. Thus the input file containing the regex expression becomes:
cmd.exe in turn invokes the tool (note one caret that disappears, probably due to the special meaning it has traditionally to the shell) This time the tool receives the regex just as we wanted it: One last run to look how it all comes together when invoking the tool directly, without using The external provider invoking the tool is amended so that the tool and the parameters are all specified separately:
The invoke sequence is straightforward this time: The arguments that reach the tool are as expected: Note that the last run only removed – as Martin said – a single layer of the processing done, namely |
Thanks for following up with all that extra detail, @luckerby! I was researching this situation recently for an unrelated project (outside of Terraform) so it was a nice coincidence that I happened to find this issue while these details were still fresh in my mind! All of what you've shown here makes sense and is consistent with what I saw in my own investigations. I vaguely remember learning that the rules for .NET CLR applications I'm going to leave this open for the moment so we can think about whether there's a way to distill this into some general information for the docs. So far I've found it difficult to do this just because of how much variation there is depending on how programs are being launched, but I think at least we could say something about not using Thanks again for the extra notes here! |
Probably the I-only-have-2-fields-available-to-invoke-a-tool mindset comes naturally to people with a Windows background (myself included) due to the tools we're usually working with. First of all, Powershell scripts will usually get invoked using some scheduled task resembling what's seen below. You have to make sure that all those arguments are squeezed together on that second line. Secondly, psexec.exe can be used to do all sorts of cool things. But there's a catch, running commands with parameters requires a special syntax, whereby cmd.exe has to be used, followed by a long line (string) of parameters (here, section Internal commands). Now in a page containing documentation, most folks I know (myself included) will usually reach for the examples section, and don't really bother with the rest unless the thing they're trying to achieve won't work. A 2nd example in the external providers page that specifically uses more than 2 parameters will probably do the trick for most. |
I think this tendency for splitting it into two elements "program" and "arguments" comes from how the Windows API itself works, where The separate project I was referring to earlier was my With that said, I think you're right that a different example could make the point effectively. I think it may still be worth describing exactly how those separate strings get combined into a single string on Windows, since that itself implies some particular assumptions about quoting that may not be initially obvious, but an example will draw attention to the fact that there can be more than two arguments here without any need to refer to the prose docs. |
No idea how relevant this is but I used to work with PHP on Windows at time when some of you were not even the idea of existence (Yes, I'm old). One of the "difference" is the way to escape Back then, we used the Had to go hunting for a test on this : https://github.com/php/php-src/blob/ab5edb6a8e71eab4aa6d85953326548eb6d9c484/ext/standard/tests/streams/bug78883.phpt#L16 It MAY still be relevant and allow you to send the double quotes correctly so that they get picked up by YMMV. |
And https://web.archive.org/web/20220629212707/https://windowsinspired.com/understanding-the-command-line-string-and-arguments-received-by-a-windows-program/ which is SOOOOO old it is in the internet archive!!!! |
Thanks for sharing that, @rquadling! I now have even less context about this in my working memory than I did last time I commented 🙃 but I can see in my My recollection from researching that is that if you're running a command through I did include replacing An important thing to keep in mind when thinking about the behavior of the In that case then, perhaps a suitable recipe when using
The (I've not actually tested this, so I can't say whether I'm making correct assumptions or not. If someone tries this and finds whether or not it works, it would be helpful to share what you tried and what happened when you tried it!) |
Hi there,
I'm running Terraform v0.11.11, and I’m calling an external tool that takes a total of 3 parameters in the following order: a regex expression and 2 strings. Since the regex being passed can be quite complex, I need to have it enclosed in double quotes. The trouble is that when terraform invokes this tool, things get messed up, as follows.
Using cmd.exe:
![image](https://user-images.githubusercontent.com/31319583/51129949-79a91180-1834-11e9-8a8c-c715ab0f89de.png)
The tools is invoked, however the escape characters are send as-is, while an extra double quote is added at the very end for some unknown reason:
![image](https://user-images.githubusercontent.com/31319583/51129990-91809580-1834-11e9-81c3-a2f2bc5bc9b7.png)
Using Powershell:
![image](https://user-images.githubusercontent.com/31319583/51130030-aeb56400-1834-11e9-96b8-dca9fd0023e5.png)
The tool is invoked, however no quotes are placed around the first parameter, as I’d want to. There’s just one quote, but that corresponds to the one with which the whole command line starts with; since the double quotes are enclosing the tool’s filename, it’s not a problem. But the regex itself is not surrounded by anything:
![image](https://user-images.githubusercontent.com/31319583/51130050-be34ad00-1834-11e9-9ec6-53da783dc7de.png)
I believe there are 2 things I need to get right – the string as entered in config.tf needs to be syntactically correct from terraform’s standpoint, then it also needs to be parsed appropriately by the shell I’m invoking. I’m somehow missing one or both.
The text was updated successfully, but these errors were encountered: