How to interpolate expressions in Terraform? - terraform

I'm trying to use the keys expression in Terraform to grab a list of keys (from a map variable) and assign it to a local variable. Here is the code snippet:
locals {
project_name_list = keys(${var.project_map})
}
However, I'm getting the following error:
Unknown token: 29:22 IDENT keys
Am I missing something here. Nowhere can I find an example of this expression. As bad as it is, even the official documentation does not help -https://www.terraform.io/docs/configuration/functions/keys.html
HashiCorp has really done a bad job of elaborating the nuances of Terraform for beginners on their website.

Terraform functions need to be wrapped in expression syntax to show that it's not a literal value: "${}"
So try this: project_name_list = "${keys(var.project_map)}"
The example in the documentation is written as though being run from the terraform command line, which already assumes the command is a HCL expression and doesn't require that syntax.
UPDATE
I said above that the expression syntax is to show that it's not a literal value. It's probably more accurate to speak of it as expression syntax vs. configuration syntax. Configuration syntax is the first level of interpolation, which forms the basic structure of your terraform file with resource blocks, data blocks, etc. The second interpolation level is expression syntax which is used to generate values used by your configuration.
Thinking of it in these terms makes better sense of the error message, Unknown token, because terraform is attempting to read it as a configuration key word.
I had compared it to a literal value because it's in the same position as where a literal value would be.

Related

terraform console: Extra characters after expression

I installed terraform and trying to do eval basic expression.
I am getting weird errors.
e.g what's wrong with this expression?
variable "BN" { default = "X" }
or
x = 3
for loops works but it needs to be wrapped into square brackets.
[ for bn in [1, 2, 3] : "%{if bn == 2} ok %{else} bad ${bn} %{endif}" ]
terraform console makes impression that you made a syntax error, but I think at most it could be semantic one.
As for now this utility looks very limited.
Is there any other place I can play with terraform where?
Of the three examples you shared, only the third one is actually a Terraform language expression. An expression is something that generates a value which you can then assign to a resource type argument.
Your first example is a variable declaration. The string you assigned to its default argument is an example of an expression - a literal string expression - but that overall block declares a new variable in a configuration, so it's not something you can evaluate to produce a value.
Your second example seems to assigning the value 3 to an argument called x, but that doesn't mean anything without some additional context: x would need to be an argument inside a block in order to be meaningful, and even then it wouldn't be an expression, but rather the definition of an argument called x using the expression 3.
If you want to experiment with the Terraform language then the best way to do it is to make a file whose name has the suffix .tf in a new directory, and then run the main Terraform commands terraform init, terraform plan, etc in that directory. The terraform console command is for evaluating hypothetical expressions against your configuration, but until you've actually written a configuration you'll have nothing other than literal values to refer to.

What $() syntax means for Groovy language?

I found this in Groovy Syntax documentation at 4.6.1. Special cases:
As slashy strings were mostly designed to make regexp easier so a few
things that are errors in GStrings like $() or $5 will work with
slashy strings.
What $() syntax means? give some usage examples please
I also found it at Define the Contract Locally in the Repository of the Fraud Detection Service:
body([ // (4)
"client.id": $(regex('[0-9]{10}')),
loanAmount : 99999
])
but I don't understand what $() means when used with regex('[0-9]{10}').
It means nothing (or what you make of it). There are two places, you
are addressing, but they have nothing to do with each other.
The docs just mention this as "you can use slashy strings to write
things, that would give you an error with a GString" - the same is true
for just using '-Strings.
E.g.
"hello $()"
Gives this error:
unknown recognition error type: groovyjarjarantlr4.v4.runtime.LexerNoViableAltException
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
/tmp/x.groovy: 1: token recognition error at: '(' # line 1, column 9.
"hello $()"
The parser either wants a { or any char, that is a valid first char
for a variable (neither ( nor 5 is).
The other place you encountered $() (in Spring cloud contract), this
is just a function with the name $.
Form the docs 8. Contract DSL:
You can set the properties inside the body either with the value method or, if you use the Groovy map notation, with $()
So this is just a function, with a very short name.
E.g. you can try this yourself:
void $(x) { println x }
$("Hello")

Invalid Template Control Keyword

Currently developing a TF template inclusive of some glue services. When attempting to init the template, I am prompted with the below error:
Error: Invalid template control keyword
on main.tf, in resource "aws_glue_classifier" "SrcPlanClassifier":
grok_pattern = "%{DATA:col},%{DATA:col2},%{DATA:col3},"%{DATA:col4}",% .
{DATA:col5},%{DATA:col6},%{DATA:col7},%{DATA:col8},%{DATA:col9},%{DATA:col10},% .
{DATA:col11},%{DATA:col12},%{DATA:col}13$"
"DATA" is not a valid template control keyword.
This template is the result of translating an existing CloudFormation template to Terraform, so the pattern has worked in the past. From what I can tell in the AWS documentation for Glue, the DATA term is an acceptable built-in classifier. That implies to me that this is an issue on the Terraform end. What am I missing here?
Thanks in advance for the help!
Terraform is understanding the %{ sequences as introducing a template directive, which then fails because if and for are the only keywords that are allowed to follow that %{ marker in the Terraform language.
To use %{ literally in your string, you can write %%{ instead to escape the introducer. Terraform will see %%{ and produce %{ in the resulting string:
grok_pattern = "%%{DATA:col},%%{DATA:col2},%%{DATA:col3},"%%{DATA:col4}",% .
{DATA:col5},%%{DATA:col6},%%{DATA:col7},%%{DATA:col8},%%{DATA:col9},%%{DATA:col10},% .
{DATA:col11},%%{DATA:col12},%%{DATA:col}13$"
(I think there were some line-wrapping problems in the message you shared so I've updated it as best I could for the escaping but you may need to do some additional escaping yourself. The general idea is to replace every %{ with %%{ in your quoted string.)
Another option, for complicated expressions whose readability is hurt significantly by this much escaping, is to move the relevant string into a separate file and then have Terraform read that file:
grok_pattern = file("${path.module}/grok_pattern.txt")
The file function just takes the text in the given file verbatim, and does not parse it for template sequences or any other special markers.

My concatenation in Azure .json is not working as desired

I am trying to deploy a load balancer template in which I port in various names to form a concatenation. This is so that the parameters template affects the naming of back end address pools and other outputs. My current concatenation code is this:
"[concat(resourceId('Microsoft.Network/loadBalancers/', variables('loadBalancerName'), '/frontendIpConfigurations/', variables('subnet1name'), '-FrontEnd'))]"
However, when I attempt to run this in Jenkins, I get the following error message:
"Unable to process template language expressions for resource '/subscriptions/****/resourceGroups/test-networks-hub-rg/providers/Microsoft.Network/loadBalancers/test-firewall-lb' at line '1' and column '2581'. 'Unable to evaluate template language function 'resourceId': function requires exactly one multi-segmented argument which must be resource type including resource provider namespace. Current function arguments 'Microsoft.Network/loadBalancers/,test-firewall-lb,/frontendIpConfigurations/,test-firewall-subnet,-FrontEnd'
It seems like the resourceId is not recognised because it is not being concatenated properly. I checked the Microsoft guidance, but I can't seem to work out where I am going wrong.
Can anyone see my error?
Okay, turns out I just had a bracket in the wrong place. The correct syntax is this:
"[concat(resourceId('Microsoft.Network/loadBalancers/', variables('loadBalancerName')), '/frontendIpConfigurations/', variables('subnet1name'), '-FrontEnd')]"
Remove concat() and just use the resourceID function, e.g.
[resourceId('Microsoft.Network/loadBalancers/frontendIpConfigurations', variables('loadBalancerName'), concat(variables('subnet1name'), '-FrontEnd'))]
Learning that function/pattern will help quite a bit when you start using resources in other groups or subscriptions.

proper syntax for bpel bpel:doXslTransform

I am trying to do an XSL transform on an xml structure in a bpel assignment statement. There is a syntax problem, but I am having trouble finding official documentation. There are examples all over the internet but I have not found a clear explanation. Here is my best shot. What do the last two parameters do? Why is eclipse saying the first argument must be a literal, even though test3.xsl is a string?
<bpel:assign validate="yes" name="Assign">
<bpel:copy keepSrcElementName="no">
<bpel:from>
<![CDATA[bpel:doXslTransform("test3.xsl", $personalInfoServiceOutput.parameters), "middle", $positionSkillManagementInput]]>
</bpel:from>
<bpel:to variable="positionSkillManagementInput"></bpel:to>
</bpel:copy>
</bpel:assign>
The signature of doXSLTransform looks as follows:
object bpel:doXslTransform(string, node-set, (string, object)*)
The first parameter is the name of the XSLT script, the second parameter is an XPath identifying the source document (e.g. a variable, part, nodeset, node). The third and the fourth parameter is a key-value pair, the string is the key and the object is the value. Those pairs are mapped into the script's parameter context so that you can access these values by their name in the script. There can be any number of these pairs.
The best resource to look up such things is the WS-BPEL 2.0 specification, doXSLTransform is described in Sect. 8.4
When I use the following code :
<bpel:copy keepSrcElementName="no">
<bpel:from>
<![CDATA[bpel:doXslTransform("parseSample.xsl", $output.payload)]]>
</bpel:from>
<bpel:to variable="output"></bpel:to>
</bpel:copy>
I also get the error, that first argument must be literal string.
But, when I deploy my service (with error) to wso2 bps, it works fine.
You can try with this.
I faced the same issue. Agree with NGoyal. Shows error in BPEL but works when deployed.

Resources