Terraform . How to pass multiple values in command line using list (string) in variable.tf file? - terraform

I have a simple main and variable files for deploying webapp for containers in Azure.
But I would like that terraform plan uses variables from the command line to choose names like follows:
terraform plan -var resource_group_name=my-rg
This worked perfectly commenting the name of the default value for the RG like this.
main.tf
data "azurerm_resource_group" "my-rg" {
name = var.resource_group_name
}
variable.tf
variable "resource_group_name" {
# default = "Search-API"
}
But if I want to do the same for a list string I don´t know how to do it. I want to be able to do something that If I put 2 names 2 webapps are going to be created, if I put 3, 3 webapps and so.
I tried with this (also commenting default value) :
main.tf
resource "azurerm_app_service" "azure-webapp" {
count = length(var.webapp_server_name)
name = var.webapp_server_name[count.index]
variable.tf
variable "webapp_server_name" {
description = "Create Webapp with following names"
type = list(string)
#default = ["webapp-a", "webapp-b", "webapp-c"]
But I´m getting:
terraform plan -var webapp_server_name=webapp-a
Error: Variables not allowed
on <value for var.webapp_server_name> line 1:
(source code not available)
Variables may not be used here.
I also tried with empty string like:
variable "webapp_server_name" {
description = "Create Webapp with following names"
type = list(string)
default = []
}
Is there a way to do such a thing with terraform? to define an empty list and pass values (one, two, or more) from command?
thanks
UPDATE
Tried like this, following this post but now is asking to put the value even though I´m passing it through command line
terraform plan -var 'listvar=["webapp-a"]'
var.webapp_server_name
Create Webapp with following names
Enter a value:

If there is a variable declaration:
variable "webapp_server_name" {
description = "Create Webapp with following names"
type = list(string)
#default = ["webapp-a", "webapp-b", "webapp-c"]
}
You could use it like this with \ to escape the quotes".
terraform plan -var 'webapp_server_name=[\"webapp-a\", \"webapp-b\", \"webapp-c\"]'
For example, it worked with using the latest terraform provider version Terraform v0.13.4.

When you pass in the variable from the command line with -var webapp_server_name=webapp-a you are passing it in as a string.
But you've defined the variable as a list. So based on the docs you'll want the command line to look something like:
terraform plan -var='webapp_server_name=["webapp-a"]'

Related

Terraform - Variable name for CosmosDB

I'm using Terraform to create the resources in Azure and have split the files as main.tf, variables.tf and terraform.tfvars
In order to have a standard naming convention, I'm following the process below when naming the resources.
prefix-environment-resourcename
For example, in main.tf I'm creating it as below:
resource "azurerm_resource_group" "rg" {
name = "${var.prefix}-${var.environment}-${var.resource_group_name}"
location = "westus"
}
The variables will be declared in variables.tf and the terraform.tfvars will contain
prefix = "sample"
environment = "dev"
resource_group_name = "rg"
and when the Terraform is executed, I'll get the resource name created as "sample-dev-rg"
This will come in handy when I'm creating other resources or deploy the code to other environments. Since I just need to modify the tfvars alone.
Another example:
resource "azurerm_app_service" "example" {
name = "${var.prefix}-${var.environment_name}-${var.appservice_name}"
}
My issue is:
How do I use the logic above for CosmosDb? I need the name in the main.tf to be
created without special characters.
How do I create something like
this: sampledevcosmosdbname
If you're using Terraform 0.13 and above, you can make use of regex validation for each of the variables that make up your resource names, and ensure that none of them use special/unusual characters. Here's an example prefix variable that can only use A-Z, a-z, 0-9, and - characters:
variable "prefix" {
type = string
description = "Prefix ID"
validation {
condition = can(regex("^[A-Za-z0-9-]*$", var.prefix))
error_message = "The prefix cannot use special characters."
}
}
To create something like sampledevcosmosdbname (prefix, environment, dbname), you can just place several interpolations next to one another like so - no separation is needed:
resource "azurerm_cosmosdb_sql_database" "example" {
...
name = "${var.prefix}${var.environment}${var.dbname}"
}

how to construct name on terraform v0.12

previously on v0.11 these works on my deployment
resource "aws_alb_target_group" "my_tg" {
name = "${var.SCHOOL}-${var.DEPT}-${var.ID}-tg"
However on v0.12 I'm kinda lost how to adjust with thier update, I'm trying these however gives me error
resource "aws_alb_target_group" "my_tg" {
name = "{"var.SCHOOL"}-{"var.DEPT"}-{"var.ID"}-tg"
ERROR
on alb-tg.tf line 2, in resource "aws_alb_target_group" "my_tg":
2: name = "{"var.SCHOOL"}-{"var.DEPT"}-{"var.ID"}-tg"
An argument definition must end with a newline.
In terraform v0.12 the way you interpolate variables in a string did not change.
The example you provided is still valid.
resource "aws_alb_target_group" "my_tg" {
name = "${var.SCHOOL}-${var.DEPT}-${var.ID}-tg"
The only change in v0.12 is when you are passing only a variable as the name. So the previous name = ”${var.name}” changed in name = var.name. But seeing that you are adding the dash between variables the first example you provided is a valid string and should work.

How to have a literal string of ${something} in a template data file

I have a template file, that is creating a fluentd file and inserting various variables. I am now trying to include this plugin which expects to find its own variables in the config file. The problem is that Terraform defines a variable in a template like ${variable} and this plugin expects to find its variables in the file as literal ${variable}
How can I tell terraform to not interpolate a ${} in a file, but to actually pass that entire string?
File Snippet:
<filter tomcat.logs>
#type record_transformer
<record>
customer ${customer}
environment ${environment}
application ${application}
</record>
</filter>
The above ${} are all variables I have defined for my template. I now need to add a section like this.
<record>
hostname ${tagset_name}
instance_id ${instance_id}
instance_type ${instance_type}
az ${availability_zone}
private_ip ${private_ip}
vpc_id ${vpc_id}
ami_id ${image_id}
account_id ${account_id}
</record>
Where all of those are not variables but how it actually needs to look in the rendered template. I tried swapping them to be like $${account_id}, but that just ends up rendering account_id in the file.
data "template_file" "app" {
template = "${file("templates/${var.application}.tpl")}"
vars {
customer = "${var.customer}"
environment = "${var.environment}"
application = "${var.application}"
}
}
Here is a breakdown of what is happening.
In the user data I have "instance_type $${instance_type}"
The launch configuration that is created for the instances, shows "instance_type ${instance_type}"
The actual file that is present on AWS shows "instance_type"
Finally figured this out. The answer from the marked duplicate question is incorrect for this instance.
template.tpl contains
cat <<EOT > /root/test.file
db.type=${db_type}
instance_type \$${instance_type}
EOT
Result
Error: Error refreshing state: 1 error(s) occurred:
* module.autoscaling_connect.data.template_file.app: 1 error(s) occurred:
* module.autoscaling_connect.data.template_file.app: data.template_file.app: failed to render : 27:16: unknown variable accessed: bogus_value
template.tpl contains
cat <<EOT > /root/test.file
db.type=${db_type}
instance_type \$${instance_type}
EOT
Results in a launch configuration containing
cat <<EOT > /root/test.file
db.type=mysql
instance_type \${instance_type}
EOT
Results in the File we created on the instance containing
db.type=mysql
instance_type ${instance_type}
In Short to end up with a ${something} in the file created from a terraform template file, you have to use \$${something} in the .tpl file.
In my case, having this issue inside a resource, the value is inside quotes and I don't need the \ to fix it.
somevalue = "$${variable}"
produces "${variable}" instead of "my_value"

Terraform variable files

I am trying to use a variables file to deploy resource groups in Azure using Terraform but it works if I only have one variable. If I use two I get an error:
"invalid value "variables.tf" for flag -var-file: multiple map
declarations not supported for variables"
The variables file is as below :
variable "resource_group_name" {
description = "The name of the resource group in which the resources will be created"
default = "im-from-the-variables-file"
}
variable "location" {
description = "The location/region where the virtual network is created. Changing this forces a new resource to be created."
default = "west europe"
}
The main file used to deploy is as below:
resource "azurerm_resource_group" "vm" {
name = "${var.resource_group_name}"
location = "${var.location}"
}
You've confused the variable definition syntax to the variable setting syntax.
Terraform will concatenate all the .tf files in a directory so your variables.tf file (assuming it's in the same directory as your main.tf (or whatever contains your azurerm_resource_group resources etc) is already included.
You need to define every variable before it can be used so things like:
resource "azurerm_resource_group" "vm" {
name = "${var.resource_group_name}"
location = "${var.location}"
}
by themselves would not be valid as the variables resource_group_name and location are not defined.
You define variables with the syntax you've used in your variables.tf file:
variable "location" {
description = "The location/region where the virtual network is created. Changing this forces a new resource to be created."
default = "west europe"
}
To override the defaults (if wanted or if a default isn't provided) then you need to either pass the variable in at run time (using TF_VAR_location environment variables or by using -var location="west us") or you can define vars files that take the form:
location = "west us"
resource_group_name = "im-from-the-variables-file"
Terraform will automatically load any files in the directory called terraform.tfvars or *.auto.tfvars and you can also define extra vars files at any point by using -var-file=myvars.tfvars as you have attempted to do (but with a .tf file containing HCL instead of key-pairs.
Adding to what ydaetskcoR has mentioned in above answer. If you have already specified default values in variable file for all variables defined and you need just that default values you don't even need to pass -var-file since default values will be used if you don't pass values

declare a variable using `execute` Interpolation in Terraform

I want to declare a a sub-string of a variable to another variable. I tested taking a sub-string using terraform console.
> echo 'element(split (".", "10.250.3.0/24"), 2)' | terraform console
> 3
my subnet is 10.250.3.0/24 and I want my virtual machine to get private IP address within this subnet mask 10.250.3.6. I want this to get automatically assign by looking at subnet address. What I've tried;
test.tf
variable subnet {
type = "string"
default = "10.250.3.0/24"
description = "subnet mask myTestVM will live"
}
variable myTestVM_subnet {
type = "string"
default = "10.250." ${element(split(".", var.trusted_zone_onpremises_subnet), 2)} ".6"
}
And then I test it by
terraform console
>Failed to load root config module: Error parsing /home/anum/test/test.tf: At 9:25: illegal char
I guess its just simple syntax issue. but couldn't figure out what!
As you've seen, you can't interpolate the values of variables in Terraform.
You can, however, interpolate locals instead and use those if you want to avoid repeating yourself anywhere.
So you could do something like this:
variable "subnet" {
type = "string"
default = "10.250.3.0/24"
description = "subnet mask myTestVM will live"
}
locals {
myTestVM_subnet = "10.250.${element(split(".", var.trusted_zone_onpremises_subnet), 2)}.6"
}
resource "aws_instance" "instance" {
...
private_ip = "${local.myTestVM_subnet}"
}
Where the aws_instance is just for demonstration and could be any resource that requires/takes an IP address.
As a better option in this specific use case though you could use the cidrhost function to generate the host address in a given subnet.
So in your case you would instead have something like this:
resource "aws_instance" "instance" {
...
private_ip = "${cidrhost(var.subnet, 6)}"
}
Which would create an AWS instance with a private IP address of 10.250.3.6. This can then make it much easier to create a whole series of machines that increment the IP address used by using the count meta parameter.
Terraform doesn't allows interpolations declaration of variables in default. So I get ;
Error: variable "myTestVM_subnet": default may not contain interpolations
and the syntax error really got fixed after banging my head, so here is what Terraform likes;
private_ip_address = "10.250.${element(split(".", "${var.subnet}"), 2)}.5"

Resources