Terraform multiple AWS route53 MX records - amazon-web-services

I have locals in tf to push MX records to cloudflare and it works exactly I want it to. The same locals I want to use for Route53 but for route53 the records should be combined to one. How I can use blow locals to push to Route53 using terraform.
locals {
MXRecordSets = [
{
Name = "example.com.",
Type = "MX",
TTL = 3600,
MXRecords = [
{
"Value" = "1 aspmx.l.google.com"
},
{
"Value" = "5 alt1.aspmx.l.google.com"
}
]
}
]
}
locals {
FlatMXRecordSets = merge([
for idx, MXRecordSet in local.MXRecordSets:
{
for MXRecord in MXRecordSet.MXRecords:
"${idx}-${MXRecord.Value}" => {
MXRecordSet = MXRecordSet
MXRecord = MXRecord["Value"]
}
}
]...)
}
and finally I am using below aws route53 module to push changes to AWS, but its not working:
resource "aws_route53_record" "mx_records" {
for_each =local.FlatMXRecordSets
zone_id = aws_route53_zone.carfeine_com.id
name = each.value["MXRecordSet"].Name
type = each.value["MXRecordSet"].Type
records = [ each.value["MXRecord"] ]
ttl = 1
}
The error comes as [Tried to create resource record set [name='example.com.', type='MX'] but it already exists] because I am using for_each and I should not use that. Any other way ?

Your MXRecordSets is already flat, thus it does not require flattening. But Its also a list of maps, which can lead to more issues, as a list depends on order of items. Thus its better to use just a map of maps, if possible:
locals {
MXRecordSets = {
"mail.mwotest.xyz." = {
Type = "MX",
TTL = 3600,
MXRecords = [
{
"Value" = "1 aspmx.l.google.com"
},
{
"Value" = "5 alt1.aspmx.l.google.com"
}
]
}
}
}
then
resource "aws_route53_record" "mx_records" {
for_each = local.MXRecordSets
zone_id = aws_route53_zone.carfeine_com.id
name = each.key
type = each.value.Type
records = [ for key,record in each.value["MXRecords"]: record["Value"] ]
ttl = 1
}
in the above, the
[ for key,record in each.value["MXRecords"]: record["Value"] ]
will produce correct records for MX as:
records = ["1 aspmx.l.google.com", "5 alt1.aspmx.l.google.com"]

Related

Terraform create multiple resources using for_each and jsondecode

I want to create multiple resource(GCP's multiple cloudSQL instances) and this is what I have:
locals {
psql_settings = [
{ "name" : "psql1", "location" : "us-central1", "zone" : "us-central1-c" },
{ "name" : "psql2", "location" : "us-east1", "zone" : "us-east1-b" }
]
}
I have to use them in json format because this will be stored in consul for dynamic changes.
Using this locals value, how I can create multiple resources. I am trying:
module "postgresql-db" {
depends_on = [
module.vpc
]
source = "../modules/postgres"
for_each = local.psql_settings[0]
name = each.value.name
random_instance_name = true
database_version = "POSTGRES_13"
project_id = "xyz-project
zone = each.value.zone
region = each.value.location
...
...
It should be:
for_each = {for idx,val in local.psql_settings: idx => val}
The code changes your list of maps, into map of maps, which is required by for_each.

Terraform for_each iteration over object

I'm relatively new to Terraform and I'm looking to simplify some private r53 zones I need to create using for_each.
I have the following local which I want to use to create private zones and associated A records in those zones:
locals {
private_zones = [
{
name = "foo.com"
txt = [
"This is the txt record for foo.com"]
ttl = 300
records = {
"host1" = "192.168.0.1",
"host2" = "192.168.0.2"
}
},
{
name = "bar.com"
txt = [
"This is the txt record for bar.com"]
ttl = 300
records = {
"host1" = "192.168.0.3",
"host2" = "192.168.0.4"
}
}
]
}
I've found some code which will allow me to iterate over the local to create the zones
resource "aws_route53_zone" "zone" {
for_each = { for name in local.private_zones : name.name => name }
name = each.value.name
vpc {
vpc_id = <vpc_id>
}
}
but I've no idea how I can iterate and create A records in the respective zone using the records list in each local.
You would use aws_route53_record and flattened private_zones:
locals {
private_zones_flat = merge([
for zone in local.private_zones: {
for host, ip in zone.records:
"${zone.name}-${host}" => {
zone_name = zone.name
host = host
ip = ip
}
}
]...)
}
resource "aws_route53_record" "host" {
for_each = local.private_zones_flat
zone_id = aws_route53_zone.zone[each.value.zone_name].zone_id
name = each.value.host
type = "A"
ttl = "300"
records = [each.value.ip]
}

Terraform count within for_each loop

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.

Creating dynamic resources with Terraform for_each

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.

Iterating through a map of objects with multiple values using for_each

I have multiple DNS records that I am creating using Terraform. One of my records has two values that it needs to read from. I am having an issue getting Terraform to be able to loop through my variables in order for this to read successfully. Below is my code. I am getting an error that does not make any sense even though I have the correct data type. There is some error with how I am looping through these resources, and I am having some trouble figuring out where I went wrong. Any advice would be appreciated.
variables.tf
variable "mx" {
type = map(object({
ttl = string
records = set(string)
}))
}
variables.tfvars
mx = {
"mx_record1" = {
ttl = "3600"
records = [
"mx_record1_value"
]
}
"mx_record2" = {
ttl = "3600"
records = [
"mx_record2_value"
"mx_record2_value2"
]
}
mx.tf
locals {
mx_records = flatten([
for mx_key, mx in var.mx : [
for record in mx.records : {
record = record
mx = mx_key
}
]
])
}
resource "aws_route53_record" "mx_records" {
for_each = { for mx in local.mx_records : mx.record => mx }
zone_id = aws_route53_zone.zone.zone_id
name = each.key
type = "MX"
ttl = each.value.ttl
records = [
each.value.record
]
}
Got below error
Error:
Error: Unsupported attribute
on mx.tf line 17, in resource "aws_route53_record" "mx_records":
17: ttl = each.value.ttl
|----------------
| each.value is object with 2 attributes
This object does not have an attribute named "ttl".
UPDATE:
locals {
mx_records = flatten([
for mx_key, mx in var.mx : [
for record in mx.records : {
record = record
mx = mx_key
ttl = mx.ttl
}
]
])
}
resource "aws_route53_record" "mx_records" {
for_each = { for mx in local.mx_records : mx.record => mx }
zone_id = aws_route53_zone.zone.zone_id
name = each.key
type = "MX"
ttl = each.value.ttl
records = [
each.value.record
]
}
ERROR
Error: [ERR]: Error building changeset: InvalidChangeBatch: [FATAL problem: UnsupportedCharacter (Value contains unsupported characters) encountered with ' ']
status code: 400, request id: a27e6a47-c10f-42ce-be94-10aaa9c276f8
on mx.tf line 13, in resource "aws_route53_record" "mx_records":
13: resource "aws_route53_record" "mx_records" {
I think your mx_records should be:
locals {
mx_records = flatten([
for mx_key, mx in var.mx :
[for record in mx.records: {
record = record
mx = mx_key
ttl = mx.ttl
}]
])
}
This will result in the following structure:
mx_records = [
{
"mx" = "mx_record1"
"record" = "mx_record1_value"
"ttl" = "3600"
},
{
"mx" = "mx_record2"
"record" = "mx_record2_value"
"ttl" = "3600"
},
{
"mx" = "mx_record2"
"record" = "mx_record2_value2"
"ttl" = "3600"
},
]