I'm trying to create GCP SQL DBs by iterating a list of string using Terraform's for_each and count parameter and the other loop is for the map keys (maindb & replicadb).
Unfortunately, I get the error that appears below.
Is it possible to do this is Terraform?
variables.tf
variable "sql_var" {
default = {
"maindb" = {
"db_list" = ["firstdb", "secondsdb", "thirddb"],
"disk_size" = "20",
},
"replicadb" = {
"db_list" = ["firstdb"],
"disk_size" = "",
}
}
}
main.tf
resource "google_sql_database_instance" "master_sql_instance" {
...
}
resource "google_sql_database" "database" {
for_each = var.sql_var
name = "${element(each.value.db_list, count.index)}"
instance = "${google_sql_database_instance.master_sql_instance[each.key].name}"
count = "${length(each.value.db_list)}"
}
Error Message
Error: Invalid combination of "count" and "for_each"
on ../main.tf line 43, in resource
"google_sql_database" "database": 43: for_each =
var.sql_var
The "count" and "for_each" meta-arguments are mutually-exclusive, only
one should be used to be explicit about the number of resources to be
created.
What the error message tells you is that you cannot use count and for_each together. It looks like you are trying to create 3 main databases and 1 replica database am I correct? What I would do is create your 2 master instances and then transform your map variable to create the databases.
terraform {
required_version = ">=0.13.3"
required_providers {
google = ">=3.36.0"
}
}
variable "sql_instances" {
default = {
"main_instance" = {
"db_list" = ["first_db", "second_db", "third_db"],
"disk_size" = "20",
},
"replica_instance" = {
"db_list" = ["first_db"],
"disk_size" = "20",
}
}
}
locals {
databases = flatten([
for key, value in var.sql_instances : [
for item in value.db_list : {
name = item
instance = key
}
]
])
sql_databases = {
for item in local.databases :
uuid() => item
}
}
resource "google_sql_database_instance" "sql_instance" {
for_each = var.sql_instances
name = each.key
settings {
disk_size = each.value.disk_size
tier = "db-f1-micro"
}
}
resource "google_sql_database" "sql_database" {
for_each = local.sql_databases
name = each.value.name
instance = each.value.instance
depends_on = [
google_sql_database_instance.sql_instance,
]
}
Then, first run terraform apply -target=google_sql_database_instance.sql_instance and after this run terraform apply.
Related
I am working with Terraform and I need to create a Glue Workflow. My target is the following schema:
I don't understand how I can use the "nested loops" to create the resouce from a variable object with a list of string:
My main.tf file is:
provider "aws" {
region = "eu-west-1"
profile = "<MY-STAGE>"
}
locals {
workflow_name = "my_example"
first_job = "Job_start"
my_map = [
{
flow = ["JOB-A1", "JOB-A2", "JOB-A3"]
},
{
flow = ["JOB-B1", "JOB-B2", "JOB-B3"]
}
]
}
resource "aws_glue_workflow" "example" {
name = "example"
}
resource "aws_glue_trigger" "example-start" {
name = "trigger-start"
type = "ON_DEMAND"
workflow_name = local.workflow_name
actions {
job_name = "${replace(lower(local.first_job), "_", "-")}"
}
}
resource "aws_glue_trigger" "this" {
for_each = toset(local.my_map)
name = "trigger-inner--${lower(element(each.key, index(local.my_map, each.key)))}"
type = "CONDITIONAL"
workflow_name = aws_glue_workflow.example.name
predicate {
conditions {
job_name = "${replace(lower(element(each.key, index(local.my_map, each.key))), "_", "-")}"
state = "SUCCEEDED"
}
}
actions {
job_name = "${replace(lower(element(each.key, index(local.my_map, each.key) + 1)), "_", "-")}"
}
}
When I try to do the "plan" I get this error:
| each.key is a string, known only after apply
| local.my_map is tuple with 2 elements
| Call to function "element" failed: cannot read elements from string.
Then, how can I get all the rows of the object and scroll through the list elements?
Any help or pointers would be much appreciated!
I want to create two Amazon SNS topics with the same aws_iam_policy_document, aws_sns_topic_policy & time_sleep configs.
This is my terraform, my_sns_topic.tf:
resource "aws_sns_topic" "topic_a" {
name = "topic-a"
}
resource "aws_sns_topic" "topic_b" {
name = "topic-b"
}
data "aws_iam_policy_document" "topic_notification" {
version = "2008-10-17"
statement {
sid = "__default_statement_ID"
actions = [
"SNS:Publish"
]
# Cut off some lines for simplification.
## NEW LINE ADDED
statement {
sid = "allow_snowflake_subscription"
principals {
type = "AWS"
identifiers = [var.storage_aws_iam_user_arn]
}
actions = ["SNS:Subscribe"]
resources = [aws_sns_topic.topic_a.arn] # Troubles with this line
}
}
resource "aws_sns_topic_policy" "topic_policy_notification" {
arn = aws_sns_topic.topic_a.arn
policy = data.aws_iam_policy_document.topic_policy_notification.json
}
resource "time_sleep" "topic_wait_10s" {
depends_on = [aws_sns_topic.topic_a]
create_duration = "10s"
}
As you can see here, I set up the configuration only for topic-a. I want to loop this over to apply for topic-b as well.
It would be better to use map and for_each, instead of separately creating "a" and "b" topics:
variable "topics" {
default = ["a", "b"]
}
resource "aws_sns_topic" "topic" {
for_each = toset(var.topics)
name = "topic-${each.key}"
}
data "aws_iam_policy_document" "topic_notification" {
version = "2008-10-17"
statement {
sid = "__default_statement_ID"
actions = [
"SNS:Publish"
]
# Cut off some lines for simplification.
}
resource "aws_sns_topic_policy" "topic_policy_notification" {
for_each = toset(var.topics)
arn = aws_sns_topic.topic[each.key].arn
policy = data.aws_iam_policy_document.topic_policy_notification.json
}
resource "time_sleep" "topic_wait_10s" {
for_each = toset(var.topics)
depends_on = [aws_sns_topic.topic[each.key]]
create_duration = "10s"
}
I have a set of AliasRecords under terraform locals and wanted to map them under terraform's resource "aws_route53_record" . Below is the locals value:
locals {
AWSAliasRecordSets = [
{
Name = "api-dev.example.com.",
Type = "A",
AliasTarget = {
HostedZoneId = "EXAMPLE",
DNSName = "kjhskdjhf.cloudfront.net.",
EvaluateTargetHealth = false
}
},
{
Name = "api.example.com.",
Type = "A",
AliasTarget = {
HostedZoneId = "EXAMPLE",
DNSName = "jsdhgfjkdshf.cloudfront.net.",
EvaluateTargetHealth = false
}
}
]
}
What I am doing is :
locals {
FlatAWSAliasRecordSets = merge([
for idx, AWSAliasRecordSet in local.AWSAliasRecordSets:
{
for AliasTarget in AWSAliasRecordSet.AliasTarget:
"${idx}-${AliasTarget}" => {
HostedZoneId = AliasTarget["HostedZoneId"]
DNSName = AliasTarget["DNSName"]
EvaluateTargetHealth = AliasTarget["EvaluateTargetHealth"]
}
}
]...)
}
resource "aws_route53_record" "alias_records" {
for_each = local.FlatAWSAliasRecordSets
zone_id = each.value["HostedZoneId"]
name = each.value["AliasTarget"].Name
type = each.value["AliasTarget"].Type
alias {
zone_id = each.value["HostedZoneId"]
name = each.value["AliasTarget"].Name
evaluate_target_health = each.value["EvaluateTargetHealth"]
}
}
and when pushing to AWS ( terraform apply), it fails with below error:
│ on main.tf line 508, in locals:
│ 508: EvaluateTargetHealth = AliasTarget["EvaluateTargetHealth"]
│ This value does not have any indices.
Your AWSAliasRecordSets does not require flattening, as it is already flat. Thus you can go use regular count for it.
resource "aws_route53_record" "alias_records" {
count = length(local.AWSAliasRecordSets)
zone_id = local.AWSAliasRecordSets[count.index]["AliasTarget"].HostedZoneId
name = local.AWSAliasRecordSets[count.index].Name
type = local.AWSAliasRecordSets[count.index].Type
alias {
zone_id = local.AWSAliasRecordSets[count.index]["AliasTarget"].HostedZoneId
name = local.AWSAliasRecordSets[count.index].DNSName
evaluate_target_health = each.value["AliasTarget"].EvaluateTargetHealth
}
}
You also have to double check your use of Name and DNSName. Your current usage does not seem right to me, but this would be a new issue if this is really the case.
I would like to create AWS SSM Parameters using Terraform, with the parameters being passed in as input variables.
I see there is a for_each feature, but how can this be applied to top level properties within a terraform resource? From the documentation, the use of for_each appears to be restricted to not work on top level properties of a resource, am I misunderstanding?
This is what I am trying to accomplish:
main.tf
resource "aws_ssm_parameter" "ssm_parameters" {
for_each = var.params
content {
name = name.value
type = "String"
overwrite = true
value = paramValue.value
tags = var.tags
lifecycle {
ignore_changes = [
tags,
value
]
}
}
}
variables.tf
variable "params" {
default = [
{
name = "albUrl"
paramValue = "testa"
},
{
name = "rdsUrl1"
paramValue = "testb"
},
{
name = "rdsUrl2"
valparamValueue = "testc"
},
]
}
You can use for each, but you need to modify its syntax and fix syntax in your var.params:
variable "params" {
default = [
{
name = "albUrl"
paramValue = "testa"
},
{
name = "rdsUrl1"
paramValue = "testb"
},
{
name = "rdsUrl2"
paramValue = "testc"
},
]
}
Then to use for each, and create 3 ssm parameters:
resource "aws_ssm_parameter" "ssm_parameters" {
for_each = {for v in var.params: v.name => v.paramValue}
type = "String"
name = each.key
value = each.value
overwrite = true
}
In the above you have to project your list(map) to a map as it is required for for_each.
I have two sets of CIDRs for each environment, i would like for terraform to shuffle between the two sets whenever a new instance is being created.
I have looked at the terraform random_shuffle provider and the merge function but these do not provide the solution to my problem.
resource "aws_subnet" "myapp" {
cidr_block = "${cidrsubnet(var.vpc_cidr[terraform.workspace], 5, count.index + 16 + 5)}"
}
variable "vpc_cidr" {
type = "map"
default = {
QA = "20.30.100.0/23"
TEST = "20.37.200.0/23"
PROD = "20.37.200.0/23"
DEV = "20.37.100.0/23"
}
}
locals {
"vpc_cidr_2" = {
QA = "10.30.182.0/23"
TEST = "10.37.238.0/23"
PROD = "<none>"
DEV = "<none>"
}
}
I would like cidr_block to be calculated based on either vpc_cidr or vpc_cidr2 and shuffle between each. Also it would need to check for and fall back to the other map if is found.
Note: vpc_cidr is a variable while vpc_cidr_2 is a local.
random_shuffle is the right resource you should work on, but you need change the idea to mix variable and locals together.
resource "aws_subnet" "myapp" {
vpc_id = "${aws_vpc.main.id}"
cidr_block = "${cidrsubnet(lookup(var.vpc_cidr[random_shuffle.vpc_cidr.result], terraform.workspace), 5, count.index + 16 + 5)}"
}
resource "random_shuffle" "vpc_cidr" {
input = ["vpc_cidr", "vpc_cidr_2"]
}
variable "vpc_cidr" {
type = "map"
default = {
vpc_cidr = {
"QA" = "20.30.100.0/23"
"TEST" = "20.37.200.0/23"
"PROD" = "20.37.200.0/23"
"DEV" = "20.37.100.0/23"
}
vpc_cidr_2 = {
"QA" = "10.30.182.0/23"
"TEST" = "10.37.238.0/23"
"PROD" = "<none>"
"DEV" = "<none>"
}
}
}