How to join terraform subnet variable - amazon-web-services

variable.tf
variable "private_subnets" {
type = list
default = ["subnet-abc1,subnet-abc2"]
}
main.tf
resource "aws_db_subnet_group" "rds_subnet_group" {
name = var.cluster_name
subnet_ids = "${var.private_subnets}"
tags = {
Name = var.cluster_name,
environment = var.environment
}
}
This the present code i want use varable.tf for subnet this is way i want achive like this subnet-abc1,subnet-abc2

Considering that your variable has a wrongly defined default value, the first thing to fix is that:
variable "private_subnets" {
type = list(string)
default = ["subnet-abc1", "subnet-abc2"]
}
The way you are currently defining the default value, i.e, ["subnet-abc1,subnet-abc2"], is a list, however, it is a list with one element. Each element in a list of strings needs to start and end with double quotes, i.e. "some value". You can read more about lists in [1].
Then, you would just need to fix the code in the main.tf to look like this:
resource "aws_db_subnet_group" "rds_subnet_group" {
name = var.cluster_name
subnet_ids = var.private_subnets
tags = {
Name = var.cluster_name,
environment = var.environment
}
}
The syntax for subnets in the main.tf file is the old terraform syntax, so this will work without double quotes and ${}.
[1] https://developer.hashicorp.com/terraform/language/expressions/types#list

i have sort issue with split this is the answer
variable.tf
variable "private_subnets" {
default = "subnet-abc1,subnet-abc2"
}
main.tf
resource "aws_db_subnet_group" "rds_subnet_group" {
name = var.cluster_name
subnet_ids = "${split(",", var.private_subnets)}"
tags = {
Name = var.cluster_name,
environment = var.environment
}

Related

Error Using Variable as a Tag in Terraform

I have a question about tags in Terrafrom. I have this variables, and I'd like to use the Transit variable description name as a tag in my main.tf file. How do I go about it?
#VPC CIDRs
variable "All_VPCs" {
type = map(any)
default = {
Dev_VPC = {
ip = "10.0.3.0/24"
instance_tenancy = "default"
}
Transit_VPC = {
ip = "10.0.4.0/23"
instance_tenancy = "default"
description = "Transit_VPC"
}
}
}
I used this, but it didn't work.
resource "aws_internet_gateway" "Transit_Internet_Gateway" {
vpc_id = var.All_VPCs.Transit_VPC
tags = {
Name = "${var.All_VPCs.Transit_VPC.description}" + " Internet_Gateway"
}
You can't concatenate strings in Terraform with a + operator. The correct method of doing this is to use string interpolation (which you are already partially doing):
tags = {
Name = "${var.All_VPCs.Transit_VPC.description} Internet_Gateway"
}

Reference module with multiple resources

I am trying to create an AWS route53 hosted zone and add records to it. I added the following resources to a module main.tf
resource "aws_route53_zone" "zone" {
name = var.name
}
data "aws_route53_zone" "zone_data" {
name = var.name
}
resource "aws_route53_record" "record" {
zone_id = data.aws_route53_zone.zone_data.zone_id
name = var.record_name
type = var.record_type
ttl = var.record_ttl
records = var.record_value
}
Then I reference that module in a stack main.py as follows:
module "route53" {
source = "../../modules/route53"
name = "website.com"
type = "NS"
ttl = "30"
}
My issue is that building the stack will use the same name variable for both zone and record resources. How do I add another name to the stack module route53 for the record resource that is different from the zone resource?
If all you're trying to do in the module is create a zone and a record, you could use split to get the zone from the record name given. Like this:
main.tf
module "route53" {
source = "./modules/route53"
name = "something.website.com"
type = "NS"
ttl = "30"
}
modules/route53/main.tf
variable "name" {}
variable "type" {}
variable "ttl" {}
resource "aws_route53_zone" "this" {
name = split(".", var.name)[0]
}
resource "aws_route53_record" "this" {
zone_id = aws_route53_zone.this.zone_id
name = var.name
type = var.type
ttl = var.ttl
records = [var.name]
}
If however, you want multiple records in that zone, you could consider something like this, but this will depend heavily on what record configuration you're after.
main.tf
module "route53" {
source = "./modules/route53"
name = "website.com"
record_configs = {
something = {
type = "A"
records = ["192.168.0.99"]
}
other = {
type = "CNAME"
records = ["something.website.com"]
}
}
}
modules/route53/main.tf
variable "name" {}
variable "record_configs" {}
resource "aws_route53_zone" "this" {
name = var.name
}
resource "aws_route53_record" "this" {
for_each = var.record_configs
zone_id = aws_route53_zone.this.zone_id
name = each.key
type = each.value.type
records = each.value.records
}
If you have multiple records and names, the best way is to use for_each. For example:
variable "names" {
default = ["website.com", "website1.com", "website2.com"]
}
then
module "route53" {
source = "../../modules/route53"
for_each = toset(var.name)
name = each.key
type = "NS"
ttl = "30"
}
This way you can have same module for multiple names.
Randomly experimenting with solutions got me to add the resource variables' names as arguments to the module. This seems to allow referring to arguments of a specific resource in the root module if its argument name is the same as other resources (e.g. record_name vs name).
module "route53" {
source = "../../modules/route53"
name = "website.com"
record_name = "_auth.website.com"
record_type = "NS"
record_ttl = "30"
}

How use values of list terraform variable in aws_policy.arn

main.tf
module "iam_assumable_role" {
for_each = var.service_accounts
source = "../../../../../../modules/iam-assumable-role-with-oidc/"
create_role = true
role_name = each.value.name
provider_url = replace(module.eks.cluster_oidc_issuer_url, "https://", "")
// role_policy_arns = [for i in each.value.policies : "aws_iam_policy.${i}.arn"]
oidc_fully_qualified_subjects = each.value.wildcard == "" ? ["system:serviceaccount:${each.value.namespace}:${each.value.name}"] : []
oidc_subjects_with_wildcards = each.value.wildcard != "" ? ["system:serviceaccount:${each.value.namespace}:${each.value.wildcard}"] : []
tags = var.tags
}
resource "aws_iam_policy" "dev-policy1" {
name_prefix = "dev-policy"
description = "some description"
policy = data.aws_iam_policy_document.dev-policy1.json
}
variable "service_accounts" {
type = map(object({
name = string
namespace = string
wildcard = string
policies = list(any)
}))
}
tfvars
service_accounts = {
"dev-sa" = {
"name" = "dev-sa",
"namespace" = "dev",
"wildcard" = "*",
"policies" = ["dev-policy1", "dev-policy2"]
},
"qa-sa" = {
"name" = "qa-sa",
"namespace" = "qa",
"wildcard" = "*",
"policies" = ["qa-policy1", "qa-policy2"]
}
}
My code is iterating over service_accounts variable and creates appropriate resources. The problem is that in the commented line I cannot get the list of aws_iam_policy.arn s for the provided policy names (policy names are provided through service_account variable). My current code returns the aws_iam_policy.PolicyName.arn as string and not the actual value. Note that dev-policy1 resource s just one of the all policy resources. All policy documents exist as well. module itself is working correctly when I provide policy list directly and not through variable.
Is it possible to achieve the desired in terraform at all?
You have to use for_each, to create your policies, as you can't dynamically references individual resources the way you are trying to do:
# get all policy names. Your names are unique, so its fine to use list
locals {
policy_names = flatten(values(var.service_accounts)[*]["policies"])
}
# create policy for each name in `policy_names`
resource "aws_iam_policy" "policy" {
for_each = local.policy_names
name_prefix = "dev-policy"
description = "some description"
# similar must be done below
# policy = data.aws_iam_policy_document.dev-policy1.json
}
Then you refer to them as:
role_policy_arns = [for i in each.value.policies: aws_iam_policy[${i}].arn]

Use output value in terraform object

I have multiple output variable, I want to make one parent out variable and then put other outputs into it. I have searched about it and found that we can user terraform object for it but can't get the syntax right.
Output.tf
output "public_subnet" {
value = "${module.my_vpc.public_subnets_ids}"
}
output "vpc_id" {
value = "${module.my_vpc.vpc_id}"
}
output "private_subnet" {
value = "${module.my_vpc.private_subnets_ids}"
}
I want my output to be in a object or you can say parent output variable that have all child output vales, I have come up with few line which I know is not right syntax wise but will get you a picture of what I am thinking of.
output "vpc" {
value = {
vpc_id = "${module.my_vpc.vpc_id}"
public_subnet = "${module.my_vpc.public_subnets_ids}"
private_subnet = "${module.my_vpc.private_subnets_ids}"
}
type = object({ vpc_id = string, public_subnet = string, private_subnet = string })
}
Terraform output does not have type. Therefore, your vpc should be:
output "vpc" {
value = {
vpc_id = "${module.my_vpc.vpc_id}"
public_subnet = "${module.my_vpc.public_subnets_ids}"
private_subnet = "${module.my_vpc.private_subnets_ids}"
}
}
But the issue is that a child module has no access to its parrent's outputs. Thus, I'm not exactly sure what do you want to achieve with your outputs. Normally, you would pass variables from parent to child using variable, and then you could make new output from those variables in the child module.
Update
Based on your previous questions, there is main.tf with
module "my_vpc" {
source = "./modules/vpc"
vpc_cidr = var.vpc_cidr
public_subnet = var.public_subnet
private_subnet = var.private_subnet
availability_zone = data.aws_availability_zones.azs.names
}
Therefore, you must have a folder ./modules/vpc. In the folder, there may be a file called ./modules/vpc/vpc.tf. The file will have something like this in it (variables could be in separate file as well):
variable "vpc_cidr" {}
variable "public_subnet" {}
variable "private_subnet" {}
variable "availability_zone" {}
# the rest of the VPC definition. Since the file is not given,
# i can only speculate on the exact details of the content
resource "aws_subnet" "public" {
count = length(var.public_subnet)
vpc_id = aws_vpc.my_vpc.id
# other attributes
}
resource "aws_subnet" "private" {
count = length(var.private_subnet)
vpc_id = aws_vpc.my_vpc.id
# other attributes
}
If so, then you can create a new file, called ./modules/vpc/output.tf with the content:
output "vpc" {
value = {
vpc_id = my_vpc.vpc_id
public_subnet = aws_subnet.public.*.id
private_subnet = aws_subnet.private.*.id
}
}

Terraform: Trying to create a range of subnet cidrs using a list, but getting error "string required"

Created a list of ranges as per below
subnet_names = ["subnet-lister", "subnet-kryten", "subnet-rimmer", "subnet-cat", "subnet-holly",]
subnet_cidrs = ["192.2.128.0/18", "192.2.0.0/17", "192.2.208.0/20", "192.2.192.0/20", "192.2.224.0/20",]
With this in the subnets.tf
resource "google_compute_subnetwork" "subnet" {
name = "${var.subnet_names}-subnet"
ip_cidr_range = var.subnet_cidrs
network = var.network_name
region = var.subnet_region
And the below in variables.tf (for the module)
variable "subnet_names" {
description = "The name to use for Subnet "
type = list(string)
}
variable "subnet_cidrs" {
description = "The cidr range for for Subnets"
type = list(string)
}
But getting the following message from Terraform.
Error: Incorrect attribute value type
on ..\..\..\Test-Modules\red\dwarf\subnets.tf line 3, in resource "google_compute_subnetwork" "subnet":
3: ip_cidr_range = var.subnet_cidrs
Inappropriate value for attribute "ip_cidr_range": string required.
I'm pretty new to this, can you help me work out what I am going wrong. I've seem someone else use a list for the cidr range (mind you that was for AWS). Does GCP not support this?
It looks like what you're trying to do is actually create several subnets. For that, you should use a map variable and a loop.
variable "subnets" {
type = map(string)
}
resource "google_compute_subnetwork" "subnet" {
for_each = var.subnets
name = each.key
ip_cidr_range = each.value
...
}
Then you can provide the subnets like:
subnets = {
subnet-lister = "192.2.128.0/18",
subnet-kryten = "192.2.0.0/17",
...
}
According to the Terraform Docs, the ip_cidr_range takes only one CIDR-Block, not a list. So you need to create one resource per subnet like this:
resource "google_compute_subnetwork" "subnet" {
count = length(var.subnet_names)
name = "${var.subnet_names[count.index]}-subnet"
ip_cidr_range = var.subnet_cidrs[count.index]
network = var.network_name
region = var.subnet_region
...
I would also recommend to restructure your data a bit, so you can use for_each instead of count (look at Ben's answer). This behaves better in case you later change your configuration and for example insert a new subnet, as described nicely in this post.