I know it's possible to have straight if statements with Terraform as such.
resource "aws_iam_policy" "foo" {
name = "bar"
policy = (
var.ENVIRONMENT == "green" ?
data.aws_iam_policy_document.green.json : data.aws_iam_policy_document.red.json)
}
But is it possible to multiple if statements that would be equivalent to
if var.ENVIRONMENT == "green"
then
data.aws_iam_policy_document.green.json
else
if var.ENVIRONMENT == "red"
data.aws_iam_policy_document.red.json
else
if var.ENVIRONMENT == "blue"
data.aws_iam_policy_document.blue.json
As TF doesn't support switches, conditions work. But too much nesting can get messy to read if you start doing it throughout. Can just make 3 resource blocks and do a single condition as its cleaner to read. Can also use modules to hide this repetition if you want to go further.
https://developer.hashicorp.com/terraform/language/expressions/conditionals
However, real question is what are you trying to achieve with this? If you are doing for different envs (e.g. dev, stage & prod) why not create separate .tfvar files and when you run terraform just point to the var file which would be cleaner and better practice.
terraform apply -var-file="dev.tfvars"
https://developer.hashicorp.com/terraform/language/values/variables#variable-definitions-tfvars-files
Related
I'm using Terraform to build our AWS infrastructure projects. I need to be able to output multiple variables to a file and then to load that file back into another Terraform script.
Right now, I'm able to output the variables but they come out with the values not quoted:
variable = value
However, when loading a variable file into Terraform, it expects all values to be quoted, like this:
variable = "value"
So I can't understand why the hell Terraform doesn't just export the variables that way in the first place.
Is there any way to have it do this without requiring additional work on my part?
EDIT: I'm using Terraform v0.11.13 and cannot upgrade due to security restrictions
Output in JSON and use JQ to transform into what you like to have.
terraform output -json
main.tf
output "hogehoge" {
value = "hogehoge"
}
Execution
$ terraform apply
Outputs:
hogehoge = hogehoge
$ terraform output -json
{
"hogehoge": {
"sensitive": false,
"type": "string",
"value": "hogehoge"
}
}
However, as #ydaetskcoR commented, why not use data.terraform_remote_state?
In terraform I have 2 data outputs:
data "aws_instances" "daas_resolver_ip_1" {
instance_tags = {
Name = "${var.env_type}.${var.environment}.ns1.${var.aws_region}.a."
}
}
data "aws_instances" "daas_resolver_ip_2" {
instance_tags = {
Name = "${var.env_type}.${var.environment}.ns2.${var.aws_region}.b."
}
}
I want to get the private_ip from each of those combine those into a list and be used as follows:
dhcp_options_domain_name_servers = ["${data.aws_instances.daas_resolver_ip_1.private_ip}", "${data.aws_instances.daas_resolver_ip_1.private_ip}"]
How can I achieve this? At the moment this is the error I get:
Error: module.pmc_environment.module.pmc_vpc.aws_vpc_dhcp_options.vpc: domain_name_servers: should be a list
I believe what you've encountered here is a common limitation of Terraform 0.11. If this is a new configuration then starting with Terraform 0.12 should avoid the problem entirely, as this limitation was addressed in the Terraform 0.12 major release.
The underlying problem here is that the private_ip values of at least one of these resources is unknown during planning (it will be selected by the remote system during apply) but then Terraform 0.11's type checker is failing because it cannot prove that these unknown values will eventually produce a list of strings as the dhcp_options_domain_name_servers requires.
Terraform 0.12 addresses this by tracking type information for unknown values and propagating types through expressions so that e.g. in this case it could know that the result is a list of two strings but the strings themselves are not known yet. From Terraform 0.11's perspective, this is just an unknown value with no type information at all, and is therefore not considered to be a list, causing this error message.
A workaround for Terraform 0.11 is to use the -target argument to ask Terraform to deal with the operations it needs to learn the private_ip values first, and then run Terraform again as normal once those values are known:
terraform apply -target=module.pmc_environment.module.pmc_vpc.data.aws_instances.daas_resolver_ip_1 -target=module.pmc_environment.module.pmc_vpc.data.aws_instances.daas_resolver_ip_2
terraform apply
The first terraform apply with -target set should deal with the two data resources, and then the subsequent terraform apply with no arguments should then be able to see what the two IP addresses are.
This will work only if all of the values contributing to the data resource configurations remain stable after the initial creation step. You'd need to repeat this two-step process on subsequent changes if any of var.env_type, var.environment, or var.aws_region become unknown as a result of other planned actions.
https://www.terraform.io/docs/providers/google/r/compute_security_policy.html
rules are embedded in the google_compute_security_policy resource
And Cloud armor has a dumb limitation that only allows for up to 5 IPs in a rule- I have about 15 IPs i want to whitelist
I want this to be dynamic and not have to manually break those up into 3 rules that are statically defined in the google_compute_security_policy
I want to have a terraform var that is a list of all 15 IPs. Then loop over that var and just create 15 rules and apply to the google_compute_security_policy resource.
Is something like this possible?
You can use the builtin chunklist function:
chunklist splits a single list into fixed-size chunks, returning a list of lists.
So in your case we'd have something along these lines:
resource "google_compute_security_policy" "default" {
dynamic "rule" {
for_each = chunklist(var.my_ip_array, 5)
content {
action = "allow"
priority = rule.key+1
match {
versioned_expr = "SRC_IPS_V1"
config {
src_ip_ranges = rule.value
}
}
}
}
According to official GCP documentation (https://cloud.google.com/armor/docs/security-policy-concepts#limits) Cloud Armor has a hard limit of 5 IP addresses or IP address ranges and there is no way to change it.
You could try to split those 15 IPs on 3 different rules inside the same compute security policy to bypass this limitation.
I'm using Terraform with AWS as a provider.
I want to use a ternary operator in my availability zones local variable.
The logic is simple:
If a variable exist - take it.
If not, use the availability zones data.
The following code:
data "aws_availability_zones" "available" {}
locals {
azs = "${length(var.azs) > 0 ? var.azs : data.aws_availability_zones.available.names}"
}
variable "azs" {
description = "A list of Availability zones in the region"
default = []
type = "list"
}
Generates the following error:
conditional operator cannot be used with list values.
Although its quiet a simple operation, It turns out like a familiar issue.
I followed the work-arounds in the mentioned thread, but they looked looked quiet complicated (Using compact split and join functions together).
Any suggestions for more simple solution?
Thank you.
you are close to the answer.
Not sure how you define the variable var.azs, I guess they are defined as string and connected with commas.
So you need adjust the code, join the list to string.
locals {
azs = "${length(var.azs) > 0 ? var.azs : join(",", data.aws_availability_zones.available.names)}"
}
I create an AWS RDS instance with different KMS CMKs depending on whether or not the environment is Production or Non-Production. So I have two resources that use the terraform count if:
count = "${var.bluegreen == "nonprod" ? 1 : 0}"
This spins up an RDS instance with different KMS keys with different addresses. I need to capture that endpoint (which I do with terraform show after the build finishes) so why doesn't this work in Terraform?
output "rds_endpoint" {
value = "${var.bluegreen == "nonprod" ? aws_db_instance.rds_nonprod.address : aws_db_instance.rds_prod.address}"
}
It is an error to access attributes of a resource that has count = 0, and unfortunately Terraform currently checks both "sides" of a conditional during its check step, so expressions like this can fail. Along with this, there is a current behavior that errors in outputs are not explicitly shown since outputs can get populated when the state isn't yet complete (e.g. as a result of using -target). These annoyances all sum up to a lot of confusion in this case.
Instead of using a conditional expression in this case, it works better to use "splat expressions", which evaluate to an empty list in the case where count = 0. This would looks something like the following:
output "rds_endpoint" {
value = "${element(concat(aws_db_instance.rds_nonprod.*.address, aws_db_instance.rds_prod.*.address), 0)}"
}
This takes the first element of a list created by concatenating together all of the nonprod addresses and all of the prod addresses. Due to how you've configured count on these resource blocks, the resulting list will only ever have one element and so it will just take that element.
In general, to debug issues with outputs it can be helpful to evaluate the expressions in terraform console, or somewhere else in a config, to bypass the limitation that errors are silently ignored on outputs.