unknown token IDENT list error for IP address variable - terraform

I have defined variable in my .tfvars variable as
variables.tfvars
address_space = ["10.197.0.0/16"]
build-windows.tf
variable "address_space" {
type = list
}
In build-windows.tf file I get the error as Unknow token ident list?
Not sure what I am doing wrong here, I even do not understand why terraform wants me to use the list instead of a string. When I use string I get an error in terraform plan stating that I have to use list.
Not going anywhere.
Please assist

The type parameter is a string - try passing "list" into it.
variable "address_space" {
type = "list"
}

Related

Terraform: how to declare a variable as required (or optional) in variables.tf?

Here I have a variables.tf to define the Input variables.
# Input variable definitions
variable "project" {
description = "project name, e.g. paylocity, paychex, ultipro"
type = string
}
variable "environment" {
description = "the environment of project, e.g. production, sandbox, staging"
type = string
default = "sandbox"
}
Is there a way to mark a variable as required or optional?
The pseudo-code in my mind looks like this.
variable "project" {
description = "project name, e.g. paylocity, paychex, ultipro"
type = string
presence = required (or optional)
}
The purpose of variables.tf
variables.tf - here, you define the variables that must have values in order for your Terraform code to validate and run. You can also define default values for your variables in this file. Note that you don't need to define all of your variables in a file named
from What is the difference between variables.tf and terraform.tfvars?
Input Variables: Default Value
The variable declaration can also include a default argument. If present, the variable is considered to be optional and the default value will be used if no value is set when calling the module or running Terraform. The default argument requires a literal value and cannot reference other objects in the configuration.
From Terraform Documentation
Summary
All defined variables must have values in order to run Terraform code.
Once you set a default value for a variable, it becomes optional.
I'm using terraform 1.3.1. I think that this problem can solve with argument nullable.
The nullable argument in a variable block controls whether the
module caller may assign the value null to the variable.
This feature is available in Terraform v1.1.0 and later.
Example:
variable "example" {
type = string
nullable = false
}
Reference: https://developer.hashicorp.com/terraform/language/values/variables?optInFrom=terraform-io#disallowing-null-input-values
Since 0.14 there is an experimental (in time of writing) function that can allow you this. Here are some documents on the matter.
By default, for required attributes, Terraform will return an error if the source value has no matching attribute. Marking an attribute as optional changes the behavior in that situation: Terraform will instead just silently insert null as the value of the attribute, allowing the receiving module to describe an appropriate fallback behavior.
https://www.terraform.io/language/expressions/type-constraints#experimental-optional-object-type-attributes
https://danielrandell93.medium.com/terraform-optional-values-73407f1d5ce5

terraform replace() - Invalid value for "str" parameter: string required

When attempting to upgrade to Terraform 0.12 I get the following error:
Error: Invalid function argument
on ../../../../../modules/aws/mybox/main.tf line 85, in resource "aws_route53_record" "this":
85: name = "ip-${replace(module.this_mybox.private_ip[0], ".", "-")}"
|----------------
| module.this_mybox.private_ip[0] is tuple with 1 element
Invalid value for "str" parameter: string required.
Looking at the custom module below, I can't seem to use the replace() function in the string...
resource "aws_route53_record" "this" {
name = "ip-${replace(module.this_mybox.private_ip[0], ".", "-")}"
type = "A"
zone_id = "${var.dns_zone_id}"
records = "${module.this_mybox.private_ip[0]}"
ttl = "600"
}
The goal of the module is to spin up an EC2 based on custom parameters. Along with that, there's a few moving parts including adding a private dns record. I've named it based off of this_mybox.private_ip[0]. In Terraform 0.11.14 it worked fine; but I am roadblocked on the upgrade due to this.
Is there another approach for using replace() in the aws_route53_record name?
The error message says that module.this_mybox.private_ip[0] is a tuple and that is why replace fails. This value is also used here records = "${module.this_mybox.private_ip[0]}", which requires a list. We cannot see the value of module.this_mybox.private_ip[0] in your question, but based on the error message I would suggest to access the IP address within the tuple with module.this_mybox.private_ip[0][0].

Get type of a variable in Terraform

Is there a way to detect the type of a variable in Terraform? Say, I have a module input variable of type any, can I do some kind of switch, depending on the type?
variable "details" {
type = any
}
local {
name = var.details.type == map ? var.details["name"] : var.details
}
What I want to archive is, to be able to pass either a string as shorthand or a complex object with additional keys.
module "foo" {
details = "my-name"
}
or
module "foo" {
details = {
name = "my-name"
age = "40"
}
}
I know this example doesn't make much sense and you would like to suggest to instead use two input vars with defaults. This example is just reduced to the minimal (non)working example. The end goal is to have a list of IAM policy statements, so it is going to be a list of lists of objects.
Terraform v0.12.20 introduced a new function try which can be used to concisely select between different ways of retrieving a value, taking the first one that wouldn't produce an error.
variable "person" {
type = any
# Optional: add a validation rule to catch invalid types,
# though this feature remains experimental in Terraform v0.12.20.
# (Since this is experimental at the time of writing, it might
# see breaking changes before final release.)
validation {
# If var.person.name succeeds then var.person is an object
# which has at least the "name" attribute.
condition = can(var.person.name) || can(tostring(var.person))
error_message = "The \"person\" argument must either be a person object or a string giving a person's name."
}
}
locals {
person = try(
# The value of the first successful expression will be taken.
{name = tostring(var.person)}, # If the value is just a string
var.person, # If the value is not a string (directly an object)
)
}
Elsewhere in the configuration you can then write local.person.name to obtain the name, regardless of whether the caller passed an object or a string.
The remainder of this answer is an earlier response that now applies only to Terraform versions between v0.12.0 and v0.12.20.
There is no mechanism for switching behavior based on types in Terraform. Generally Terraform favors selecting specific types so that module callers are always consistent and Terraform can fully validate the given values, even if that means a little extra verbosity in simpler cases.
I would recommend just defining details as an object and having the caller explicitly write out the object with the name attribute, in order to be more explicit and consistent:
variable "details" {
type = object({
name = string
})
}
module "example" {
source = "./modules/example"
details = { name = "example" }
}
If you need to support two different types, the closest thing in the Terraform language would be to define two variables and detect which one is null:
variable "details" {
type = object({
name = string
})
default = null
}
variable "name" {
type = string
default = null
}
local {
name = var.name != null ? var.name : var.details.name
}
However since there is not currently a way to express that exactly one of those two must be specified, the module configuration you write must be ready to deal with the possibility that both will be set (in the above example, var.name takes priority) or that neither will be set (in the above example, the expression would produce an error, but not a very caller-friendly one).
terraform v1.0+ introduces a new function type() for this purpose. See https://www.terraform.io/language/functions/type

Terraform - How to restrict an input variable to a list of possible choices

I have a variable that the user will input during run time. Lets say the variable name is region. However, I want the execution to be only successful if the user picks a value from one of the values defined in a list/ choices.
how can I restrict it so the user's selection has to match values that are considered acceptable in the variable definition?
Stumbled across this question.
Since v0.13.0 input validation has been possible directly via the input variables. Thus you can actually achieve this with a snippet such as below.
variable "test_variable" {
type = string
description = "some test value"
validation {
condition = contains(["item1", "item2", "item3"], var.test_variable)
error_message = "Valid values for var: test_variable are (item1, item2, item3)."
}
}
Read more here - https://www.hashicorp.com/blog/custom-variable-validation-in-terraform-0-13
One solution:
variable "values_list" {
description = "acceptable values"
type = "list"
default = ["true", "false"]
}
variable "somevar" {
description = "must be true or false"
}
resource "null_resource" "is_variable_value_valid" {
count = "${contains(var.values_list, var.somevar) == true ? 0 : 1}"
"ERROR: The somevar value can only be: true or false" = true
}
If you pass a value different than "true" or "false" for the "somevar" variable, Terraform will throw an error and stop. The disadvantage is that you have to list all values in the default block of values_list.
Source: https://stackoverflow.com/a/54256780/1364793
Terraform currently has no first-class feature for this, but you can achieve the desired effect (albeit with a less-helpful error message) by asking Terraform to look the value up in a map:
variable "example" {
description = "must be a, b, or c"
}
locals {
allowed_example_values = { for v in ["a", "b", "c"] : v => v }
checked_example = local.allowed_example_values[var.example] # will fail if var.example is invalid
}
Because the values in the allowed_example_values map are the same as the keys, you can then use local.checked_example in place of var.example everywhere else in the module to set up the dependency graph such that it's impossible for an invalid value to be used.
Some caveats/limitations:
You can't customize the error message that Terraform will return when the value is invalid. Instead, Terraform will return the generic error about the key not matching any element in the map. However, it will include the source code snippet from the affected line in the error message (in Terraform 0.12 or later) so the comment at the end of that line should be visible in the error message and thus provide an additional clue to the user as to what might be wrong.
This works fully only for string values, because map keys are always strings in Terraform. Using other primitive types can work as a result of Terraform's automatic conversions from bool and number to string, but you should be sure to explicitly declare the type of the variable (using e.g. type = number) to ensure that Terraform will normalize incoming values into the expected type before looking them up in the map. This technique will not work at all for collection and structural types.
I wouldn't suggest using strings "true" and "false" since Terraform has a bool type which can represent that more intuitively. If you are using Terraform 0.11 and earlier then it's not possible to constrain to bool, but in Terraform 0.12 and later you can declare a variable as type = bool and then Terraform will handle the validation automatically.

Need to understand terraform resource structure

I am reading terraform and found below code:
resource "aws_vpc" "vpc_main" {
cidr_block = "10.0.0.0/16"
tags {
Name = "Main VPC"
}
}
Here I could not understand what vpc_main stands for in the resource definition. Could somebody explain?
It's a user-defined name for the resource - without this you wouldn't be able to distinguish multiple instances of the same resource type.
See the docs for more details.
Variable types and names in other programming languages are a good analogy. For example, in Java, you can declare a variable as follows:
String foo
The type of the variable is String and the name of the variable is foo. You must specify a name so you can (a) distinguish it from other variables of the same type and (b) refer to it later.
In Terraform, it's essentially the same idea:
resource "aws_instance" "foo" { ... }
Here, the type of the resource is aws_instance and the name is foo. You must specify a name so you can (a) distinguish it from other resources of the same type and (b) refer to it later. For example, to output the public IP address of that Instance, you could do the following:
output "public_ip" {
value = "${aws_instance.foo.public_ip}"
}

Resources