How can I dynamically use the value of an environment variable as
a value in a Terraform resource, without having to declare a corresponding Terraform variable?
For example, imagine I declare a variable that contains a list of environment variable names:
variable "env_vars" {
description = "A list of env vars to use"
}
And I define a .tfvars file that sets the value of that list:
env_vars = ["ENV_VAR_A", "ENV_VAR_B", "ENV_VAR_C"]
Then imagine some arbitrary resource with a name argument. I want to dynamically create 1 resource for each item in the env_vars list, with the value of name set to the value of the environment variable of the same name:
resource "arbitrary_thing" "thing" {
count = length(var.env_vars)
name = "<VALUE OF ENV VAR NAMED var.env_vars[count.index] >"
}
How can I correctly set that name value?
This is not a supported functionality in Terraform.
However, HashiCorp (maintainers of Terraform) have posted a support/help article on how you can achieve a workaround.
resource "arbitrary_thing" "thing" {
count = length(var.env_vars)
name =var.env_vars[count.index]
}
variable "env_vars" {
description = "A list of env vars to use"
}
And I define a .tfvars file that sets the value of that list:
env_vars = ["ENV_VAR_A", "ENV_VAR_B", "ENV_VAR_C"]
Related
Can't understand how to get value from parameter store and pass it to the terraform variable.
I have a parameter with name "github_token" and "string" type. And there is a variable in terraform code
variable "github_oauth_token" {
default=""
}
I tried to receive the value of parameter with next block of code, but it's not correct.
data "aws_ssm_parameter" "github_token" {
name = "github_token"
type = "string"
value = var.github_oauth_token
}
When using a data source, you are not creating a new value, you are simply reading an existing value. For that reason, specifying "value" and "type" doesn't make sense in this context because the value and the type of the parameter already exist. If you take a look at the data source aws_ssm_parameter documentation, you will see that the only thing that's required is the name value.
Assuming that:
The user that's running Terraform has permissions to access SSM (parameter store)
There's a value stored inside the parameter store at the path github_token
The value is not encrypted
This should work for you:
variable "github_oauth_token" {
default = "github_token"
}
data "aws_ssm_parameter" "github_token" {
name = var.github_oauth_token
}
Then to use the value of the parameter store item:
data.aws_ssm_parameter.github_token.value
I'm working on my Terraform code, and I would like to use the same code whether or a variable is set or equal "".
But if I pass the variable with "" in the aws_vpc module, there is an exception raised, saying a valid cidr is required.
If it were Ansible, I'd use the | omit filter which omit the parameter and don't pass it to the module if the variable is not set.
There is the code.
module "server_vpc" {
source = "terraform-aws-modules/vpc/aws"
name = "servers_vpc"
cidr = var.main_cidr
secondary_cidr = [var.secondary_cidr]
azs = var.azs
public_subnets = local.public_subnets
private_subnets = var.private_subnets
enable_nat_gateway = true
create_igw = true
}
On this code, if the variable is equal to "" (default), then public_subnets equals [var.main_cidr], if the variable is set, public_subnets equals [var.main_cidr, var.secondary_cidr].
But the part that didn't work like I'd like is the line with secondary_cidr = [var.secondary_cidr].
When the variable contains a valid cidr, it works, but when the variable is not set and equals to the default "", Terraform raise an error from the aws vpc module explaining that a valid cidr is needed.
So I would like a way to create this vpc with the module, but not set the parameter secondary_cidr if the variable equals "".
I hope I was clear :)
According to the documentation for this specific module secondary_cidr is not a required argument. In that case you don't have to provide an empty string. You either leave out secondary_cidr entirely or you assign null to it. Since secondary_cidr expects an array, most likely an empty array [] will work as well.
I am getting the Error: Unsupported argument, when I run terraform plan in version 12.24.
Error: Unsupported argument
on .terraform/modules/app/main.tf line 261, in resource "aws_db_instance" "db_instance":
261: timeouts = {
An argument named "timeouts" is not expected here. Did you mean to define a
block of type "timeouts"?
This is the code in tf file:
timeouts = {
create = "${var.db_instance_create_timeout}"
update = "${var.db_instance_update_timeout}"
delete = "${var.db_instance_delete_timeout}"
}
I am not sure how to fix this error.
above error was fixed by removing "=" after timeouts.
I am also getting more errors, that need solutions:
Error: Unsupported argument
on .terraform/modules/rds/main.tf line 150, in resource "aws_db_parameter_group" "db_parameter_group":
150: parameter = concat(var.parameters, local.parameters[local.parameter_lookup])
An argument named "parameter" is not expected here. Did you mean to define a
block of type "parameter"?
Code in tf file:
parameter = concat(var.parameters, local.parameters[local.parameter_lookup])
how to fix this?
I am copying the solution that worked for me from github , credits to hashicorp member bflad :
In Terraform 0.12 (or higher), the configuration language parser is stricter about the distinction between arguments and configuration blocks. This error:
An argument named "XXX" is not expected here. Did you mean to
define a block of type "XXX"?
Generally means the = (equals sign) needs to be removed from an argument assignment so it parses correctly as a configuration block, e.g.
root_block_device {
This distinction in HCL syntax may seem trivial, but under the hood this stricter type checking allowed for consistency with JSON syntax. More information about this change can be found in the Terraform 0.12 Upgrade Guide. Speaking of which, in that guide it does point to the helpful terraform 0.12upgrade command, which should automatically fix issues like these across your Terraform configurations when upgrading from Terraform 0.11. 👍
Error
An argument named "secret_environment_variables" is not expected here.
Did you mean to define a block of type "secret_environment_variables"?
Problem
main.tf
resource "google_cloudfunctions_function" "this" {
secret_environment_variables = var.secret_environment_variables
}
variables.tf
variable "secret_environment_variables" {
type = any
default = {}
description = "Secret environment variables configuration."
}
Solution
resource "google_cloudfunctions_function" "this" {
secret_environment_variables {
key = var.secret_environment_variables_key
secret = var.secret_environment_variables_secret
version = var.secret_environment_variables_version
}
}
variable "secret_environment_variables_key" {
type = string
default = null
nullable = true
description = "Name of the environment variable."
}
variable "secret_environment_variables_secret" {
type = string
default = null
nullable = true
description = "ID of the secret in secret manager (not the full resource name)."
}
variable "secret_environment_variables_version" {
type = string
default = null
nullable = true
description = "Version of the secret (version number or the string `latest`). It is recommended to use a numeric version for secret environment variables as any updates to the secret value is not reflected until new clones start."
}
I have the following code block for creating various IAM groups
resource "aws_iam_group" "environment-access" {
count = "${length(var.environments)}"
name = "access-${element(var.environments, count.index)}"
}
variable "environments" {
default = ["production", "non-production"]
type = "list"
}
I want to write the outputs of the IAM groups created in order to grab the ARN of each group to use as data via terraform_remote_state where it would look something like the following
Outputs:
access-production = arn:aws:iam::XXXXXXX:group/basepath/access-production
access-non-production = arn:aws:iam::XXXXXXX:group/basepath/access-non-production
I am having trouble creating the dynamic outputs as I am unsure how to dynamically create the output stanzas based on the the resource originally created as using the below code yields an error referencing unknown resource 'aws_iam_group.access-production' referenced.
output "access-production" {
value = "${aws_iam_group.access-production.arn}"
}
output "access-non-production" {
value = "${aws_iam_group.access-non-production.arn}"
}
An initial problem with this requirement is that it calls for having a single dynamic list of environments but multiple separate output values. In order to make this work, you'll need to either make the environment inputs separate values or produce a single output value describing the environments.
# Variant with a fixed set of environments (v0.11 syntax)
variable "production_environment_name" {
type = "string"
default = "production"
}
variable "non_production_environment_name" {
type = "string"
default = "non-production"
}
resource "aws_iam_group" "production_access" {
name = "access-${var.production_environment_name}"
}
resource "aws_iam_group" "non_production_access" {
name = "access-${var.non_production_environment_name}"
}
output "access_production" {
value = "aws_iam_group.production_access.arn"
}
output "access_non_production" {
value = "aws_iam_group.non_production_access.arn"
}
# Variant with dynamic set of environments (v0.11 syntax)
variable "environments" {
type = "list"
default = ["production", "non_production"]
}
resource "aws_iam_group" "access" {
count = "${length(var.environments)}"
name = "access-${var.environments[count.index]}"
}
output "access" {
value = "${aws_iam_group.access.*.arn}"
}
The key here is that the input variable and the output value must have the same form, so that we can make all of the necessary references between the objects. In the second example, the environment names are provided as a list, and the group ARNs are also provided as a list such that the indices correspond between the two.
You can also use a variant of the output "access" expression to combine the two with zipmap and get a map keyed by the environment names, which will probably be more convenient for the caller to use:
output "access" {
value = "${zipmap(var.environments, aws_iam_group.access.*.arn)}"
}
The new features in Terraform 0.12 allow tidying this up a bit. Here's an idiomatic Terraform 0.12 equivalent of the version that produces a map as a result:
# Variant with dynamic set of environments (v0.12 syntax)
variable "environments" {
type = set(string)
default = ["production", "non_production"]
}
resource "aws_iam_group" "access" {
for_each = var.environments
name = "access-${each.key}"
}
output "access" {
value = { for env, group in aws_iam_group.access : env => group.arn }
}
As well as having some slightly different syntax patterns, this 0.12 example has an additional practical advantage: Terraform will track those IAM groups with addresses like aws_iam_group.access["production"] and aws_iam_group.access["non_production"], so the positions of the environment names in the var.environments list are not important and it's possible to add and remove environments without potentially disturbing the groups from other environments due to the list element renumbering.
It achieves that by using resource for_each, which makes aws_iam_group.access appear as a map of objects where the environment names are keys, whereas count makes it a list of objects.
I defined a variable map my_role in terraform and set its value in abc.tfvar file as follows. if I assign account id as actual value, it works, if I set account id as a variable, it does not work. Does it mean tfvar file only allow actual value, not variable? By the way, I use terraform workspace. Therefore my_role is different based on workspace I select.
The following works:
my_role = {
dev = "arn:aws:iam::123456789012:role/myRole"
test = ...
prod = ...
}
The following does not work:
my_role = {
dev = "arn:aws:iam::${lookup(var.aws_account_id, terraform.workspace)}:role/myRole"
test = ...
prod = ...
}
The following does not work either:
lambdarole = {
dev = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/myRole"
test = ...
prod = ...
}
does
Have you tried following the example on Input Variables?
You can define your abc.tfvars file with:
variable "my_role" {
type = "map"
default = {
"dev" = "arn:aws:iam::123456789012:role/myRole"
"test" = "..."
"prod" = "..."
}
}
And access it with "${lookup(var.my_role, terraform.workspace)}".
Also, according to the from a file, the variables defined in .tfvars files are automatically loaded if you name the file terraform.tfvars, if not, you have to pass as an argument with -var-file=...
Cannot test it right now, but probably is something in this way.
I am replying when terraform 0.12 version is latest one. Solution is simple, you can create one file say vars.tf and declare variables in it.
Example - variable "xyz" {}
Now create terraform.tfvars and initialize it.
Example - xyz="abcd"
No need to pass any runtime args, it will be picked directly.
Terraform has aws_caller_identity data source. You do not need to mention or hand code account id anywhere. It can be fetched using this source.
In any of your .tf file, just include this source and then you can fetch relevant argument value.
This is how you can do it. Define this in any *.tf file
data "aws_caller_identity" "current" {}
Now where ever you want the value of arn or account id, it can be fetched using :
For account id(For terraform0.12):
data.aws_caller_identity.current.account_id
In your case, it would be like this :
dev = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/myRole"
But in order to this work, you need to define data source like shown above in any *.tf file.
For more help, refer following:
URL : https://www.terraform.io/docs/providers/aws/d/caller_identity.html