My vpc state file is at dev/vpc/main/terraform.tfstate. I would like to prevision mariaDB using the private subnets in vpc.
I got errors:
Error: Error running plan: 1 error(s) occurred:
module.csc_db_sbnet_group.var.db_subnet_group_ids: element: element() may not be used with an empty list in:
${element(data.terraform_remote_state.vpc_subnets_ids.vpc_private_subnets_ids,0)}
MariaDB code (note: MariaDB code has its own terrafrom state file):
data_sources.tf:
data "terraform_remote_state" "vpc_subnets_ids" {
backend = "s3"
config {
bucket = "dev-terraform-state"
key = "dev/vpc/main/terraform.tfstate"
region = "us-west-2"
}
}
resources.tf:
module "csc_db_sbnet_group" {
source = "modules/rds-subnet-group"
db_subnet_group_name = "${var.db_subnet_group_name}"
db_subnet_group_ids = ["${element(data.terraform_remote_state.vpc_subnets_ids.vpc_private_subnets_ids,0)}", "${element(data.terraform_remote_state.vpc_subnets_ids.vpc_private_subnets_ids,1)}"]
}
VPC code:
resources.tf:
module "vpc" {
source = "modules/vpc"
aws_region = "${var.region}"
vpc_tag_name = "${var.vpc_name}"
vpc_cidr = "${var.vpccidr}"
private-subnet-mapping = ["${var.private_az_subnets_cidr}"]
public-subnet-mapping = ["${var.public_az_subnets_cidr}"]
}
resource "aws_subnet" "add_private_subnets" {
count = "${length(var.private-subnet-mapping)}"
cidr_block = "${lookup(var.private-subnet-mapping[count.index], "cidr")}"
vpc_id = "${module.vpc.vpc_id}"
availability_zone = "${lookup(var.private-subnet-mapping[count.index], "az")}"
tags = {
Name = "${lookup(var.private-subnet-mapping[count.index], "name")}"
BU = "${lookup(var.private-subnet-mapping[count.index], "BU")}"
}
}
outputs.tf
output "vpc_private_subnets_ids" {
value = ["${aws_subnet.add_private_subnets.*.id}"]
}
output "vpc_private_subnets_cidrs" {
value = ["${aws_subnet.add_private_subnets.*.cidr_block}"]
}
When I provision MariaDB, I would like to use the private subnets provisioned in VPC code. But, it complains,
module.csc_db_sbnet_group.var.db_subnet_group_ids: element: element() may not be used with an empty list in:
${element(data.terraform_remote_state.vpc_subnets_ids.vpc_private_subnets_ids,0)}
I added code below for VPC and solved the problem.
data "aws_subnet_ids" "private_subnet_ids" {
vpc_id = "${module.vpc.vpc_id}"
}
data "aws_subnet" "private_subnet" {
count = "${length(data.aws_subnet_ids.private_subnet_ids.ids)}"
id = "${data.aws_subnet_ids.private_subnet_ids.ids[count.index]}"
}
Related
I'm trying to write Terraform code to configure a Google Cloud SQL instance to use subnet "db-subnet" of a VPC (see below).
module "vpc" {
source = "terraform-google-modules/network/google"
version = "~> 6.0"
project_id = module.project_factory.project_id
network_name = "staging-vpc"
routing_mode = "GLOBAL"
subnets = [
{
subnet_name = "db-subnet"
subnet_ip = "10.10.20.0/24"
subnet_region = var.region
subnet_private_access = "true"
subnet_flow_logs = "true"
description = "This subnet is for cloudsql DBs"
},
]
}
Next, I use 'ip_configuration:private_network' to refer to the subnet self link.
module "cloudsql_postgresql" {
source = "GoogleCloudPlatform/sql-db/google//modules/postgresql"
version = "14.0.0"
project_id = module.project_factory.project_id
name = "fhirserver-postgres"
database_version = "POSTGRES_14"
zone = "us-central1-c"
user_name = "postgres"
ip_configuration = {
ipv4_enabled = false
private_network = module.vpc.subnets["${var.region}/db-subnet"].self_link
}
}
However, it returns an error: "settings.0.ip_configuration.0.private_network" ("https://www.googleapis.com/compute/v1/projects/endue-staging-263b/regions/us-central1/subnetworks/db-subnet") doesn't match regexp "^(?:http(?:s)?://.+/)?projects/((?:(?:[-a-z0-9]{1,63}\\.)*(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?):)?(?:[0-9]{1,19}|(?:[a-z0-9](?:[-a-z0-9]{0,61}[a-z0-9])?)))/global/networks/((?:[a-z](?:[-a-z0-9]*[a-z0-9])?))$"
Can anyone help me with this error? Thank you very much!
I tried private_network = module.vpc.network_self_link and it works but it's not what I'm looking for tho.
I am new to terraform and I'm trying to create a VPC with multiple subnets and adding route tables to all those subnets in a for loop manner.
VPC: 10.207.0.0/16
There's number_of_subnets which will create subnets like this: 10.207.x.0/24
This code works fine:
variable "region" {
default = "us-east-1"
}
variable "availability_zone" {
default = "us-east-1a"
}
variable "cidr_block" {
default = "207"
}
variable "number_of_subnets" {
default = 5
}
provider "aws" {
region = var.region
}
resource "aws_vpc" "test_vpc" {
cidr_block = "10.${var.cidr_block}.0.0/16"
instance_tenancy = "default"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "test_vpc_${var.cidr_block}"
}
}
resource "aws_subnet" "test_subnets" {
count = var.number_of_subnets
vpc_id = aws_vpc.test_vpc.id
cidr_block = "10.${var.cidr_block}.${count.index+1}.0/24" # start from x.x.1.0/24
availability_zone = var.availability_zone
map_public_ip_on_launch = false
tags = {
Name = "test_subnet_${var.cidr_block}_${count.index+1}"
}
}
Now if I try to add this code to the bottom of the same file (everything is in one file called main.tf) to get the subnets and add route table to each:
# get all subnet IDs
data "aws_subnets" "q_subnets" {
filter {
name = "vpc-id"
values = [aws_vpc.test_vpc.id]
}
}
# add route table to all subnets
resource "aws_route_table_association" "rt_assoc_subnet" {
depends_on = [aws_subnet.test_subnets]
for_each = toset(data.aws_subnets.q_subnets.ids)
subnet_id = each.value
route_table_id = aws_route_table.test_rt.id
}
and run terraform apply it will give this error:
invalid for_each argument...
The "for_each" value depends on resource attribute that cannot be deteremined until apply,...
which doesn't make scense. First create vpc, then subnet, then get all subnets...
I also tried depends_on and didn't help.
How would I write this to make it work?
Any help is appreciated.
UPDATE1:
I tried to use aws_subnet.test_subnets.*.id instead of data and it still gives depencendy error:
variable "region" {
default = "us-east-1"
}
variable "availability_zone" {
default = "us-east-1a"
}
variable "cidr_block" {
default = "207"
}
variable "number_of_subnets" {
default = 5
}
provider "aws" {
region = var.region
}
resource "aws_vpc" "test_vpc" {
cidr_block = "10.${var.cidr_block}.0.0/16"
instance_tenancy = "default"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "test_vpc_${var.cidr_block}"
}
}
resource "aws_route_table" "test_rt" {
vpc_id = aws_vpc.test_vpc.id
route = []
tags = {
Name = "test_rt_${var.cidr_block}"
}
}
resource "aws_subnet" "test_subnets" {
count = var.number_of_subnets
vpc_id = aws_vpc.test_vpc.id
cidr_block = "10.${var.cidr_block}.${count.index+1}.0/24" # start from x.x.1.0/24
availability_zone = var.availability_zone
map_public_ip_on_launch = false
tags = {
Name = "test_subnet_${var.cidr_block}_${count.index+1}"
}
}
output "subnets" {
value = aws_subnet.test_subnets.*.id
}
# add route table to all subnets
resource "aws_route_table_association" "rt_assoc_subnet" {
depends_on = [aws_subnet.test_subnets]
for_each = toset(aws_subnet.test_subnets.*.id)
subnet_id = each.value
route_table_id = aws_route_table.test_rt.id
}
is there another way to pass the subnets to aws_route_table_association without getting dependency error?
Since you are using count, it is very hard to make count work with for_each. It would be better to continue using count for route table association as well. If you decide to go down that route, the only change you need is:
resource "aws_route_table_association" "rt_assoc_subnet" {
count = var.number_of_subnets
subnet_id = aws_subnet.test_subnets.*.id[count.index]
route_table_id = aws_route_table.test_rt.id
}
This will work as intended. However, if you must use for_each I would suggest defining a variable that could be used with it in all the resources you are now using count. If you really want to use for_each with the current code, then you can use the -target option [1]:
terraform apply -target=aws_vpc.test_vpc -target=aws_route_table.test_rt -target=aws_subnet.test_subnets
When running this command, this will be shown in the command output:
│ Warning: Resource targeting is in effect
│
│ You are creating a plan with the -target option, which means that the result of this plan may not represent all of the changes requested by the current
│ configuration.
│
│ The -target option is not for routine use, and is provided only for exceptional situations such as recovering from errors or mistakes, or when Terraform
│ specifically suggests to use it as part of an error message.
After the targeted resources are created, you could re-run terraform apply and it should create the route table associations.
[1] https://www.terraform.io/cli/commands/plan#target-address
this code has worked before, all I'm trying to do is add new frontend endpoints, routing rules, backend pools
I've tried only sharing the code snippets that I think are relevant but let me know if there's some key info you need missing
This one has stumped me for a couple days now and no matter what I've tried I cannot seem to make sense of the error. Its like its indexing out of the variable or searching for something that isn't there but there are something like 6 already there and now I'm adding another.
I'm worried that this front door code has not been ran in awhile and something has gotten screwed up in state. Especially given all the alerts on the accompanying TF docs for this resource - https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/frontdoor_custom_https_configuration
Its been quite awhile but the AzureRM version has gone through several updates - possibly from previous to 2.58 to now past 2.58. I guess I also don't know how to verify/look at the state file and ensure its correct - even looking at the 2.58 upgrade notes its just confusing.
Ideas?
The error
on ..\modules\frontdoor\main.tf line 129, in resource "azurerm_frontdoor_custom_https_configuration" "https_config":
129: frontend_endpoint_id = azurerm_frontdoor.main.frontend_endpoints[each.value]
|----------------
| azurerm_frontdoor.main.frontend_endpoints is map of string with 8 elements
| each.value is "www-sell-dev-contoso-com"
The given key does not identify an element in this collection value.
main.tf
provider "azurerm" {
features {}
}
terraform {
backend "azurerm" {
}
}
#the outputs.tf on this module output things like the frontdoor_endpoints
#the outputs.tf with main.tf also output similar values
module "coreInfraFrontDoor" {
source = "../modules/frontdoor"
resource_group_name = module.coreInfraResourceGroup.resource_group_name
frontdoor_name = "fd-infra-${terraform.workspace}-001"
enforce_backend_pools_certificate_name_check = lookup(var.enforce_backend_pools_certificate_name_check, terraform.workspace)
log_analytics_workspace_id = module.coreInfraLogAnalytics.log_analytics_workspace_id
tags = local.common_tags
health_probes = lookup(var.health_probes, terraform.workspace)
routing_rules = lookup(var.routing_rules, terraform.workspace)
backend_pools = lookup(var.backend_pools, terraform.workspace)
frontend_endpoints = lookup(var.frontend_endpoints, terraform.workspace)
prestage_frontend_endpoints = lookup(var.prestage_frontend_endpoints, terraform.workspace)
frontdoor_firewall_policy_name = "fdfwp${terraform.workspace}001"
frontdoor_firewall_prestage_policy_name = "fdfwp${terraform.workspace}prestage"
mode = lookup(var.mode, terraform.workspace)
ip_whitelist_enable = lookup(var.ip_whitelist_enable, terraform.workspace)
ip_whitelist = lookup(var.ip_whitelist, terraform.workspace)
key_vault_id = module.coreInfraKeyVault.id
}
module main.tf
resource "azurerm_frontdoor" "main" {
name = var.frontdoor_name
location = "global"
resource_group_name = var.resource_group_name
enforce_backend_pools_certificate_name_check = var.enforce_backend_pools_certificate_name_check
tags = var.tags
dynamic "routing_rule {#stuff is here obv}
dynamic "backend_pool {#also here}
#i think this is because there was an issue/needs to be some default value for the first endpoint?
frontend_endpoint {
name = var.frontdoor_name
host_name = "${var.frontdoor_name}.azurefd.net"
web_application_firewall_policy_link_id = azurerm_frontdoor_firewall_policy.main.id
}
#now the dynamic ones from vars
dynamic "frontend_endpoint" {
for_each = var.frontend_endpoints
content {
name = frontend_endpoint.value.name
host_name = frontend_endpoint.value.host_name
session_affinity_enabled = lookup(frontend_endpoint.value, "session_affinity_enabled", false)
web_application_firewall_policy_link_id = azurerm_frontdoor_firewall_policy.main.id
}
}
versions.tf
terraform {
required_version = "~> 0.14.7"
required_providers {
azurerm = "~>2.72.0"
}
}
variables.tf
variable "frontend_endpoints" {
type = map(any)
description = "List of frontend (custom) endpoints. This is in addition to the <frontend_name>.azurefd.net endpoint that this module creates by default."
default = {
dev = [
{
name = "dev-search-contoso-com"
host_name = "dev.search.contoso.com"
},
{
name = "dev-cool-contoso-com"
host_name = "dev.cool.contoso.com"
},
########################
#this is new below
########################
{
name = "dev-sell-contoso-com"
host_name = "dev.sell.contoso.com"
}
]
prod = [ #you get the idea ]
}
I have this script which works great. It created 3 instances with the sepcified tags to identify them easily. But issue is i want to add a remote-exec provisioner (currently commented) to the code to install some packages. If i was using count, i could have looped over it to do the remote-exec over all the instances. I could not use count because i had to use for_each to loop over a local list. Since count and for_each cannot be used together, how do i loop over the instances to retrieve their IP addresses for using in the remote-exec provisioner.
On digital ocean and AWS, i was able to get it work using host = "${self.public_ip}"
But it does not work on vultr and gives the Unsupported attribute error
instance.tf
resource "vultr_ssh_key" "kubernetes" {
name = "kubernetes"
ssh_key = file("kubernetes.pub")
}
resource "vultr_instance" "kubernetes_instance" {
for_each = toset(local.expanded_names)
plan = "vc2-1c-2gb"
region = "sgp"
os_id = "387"
label = each.value
tag = each.value
hostname = each.value
enable_ipv6 = true
backups = "disabled"
ddos_protection = false
activation_email = false
ssh_key_ids = [vultr_ssh_key.kubernetes.id]
/* connection {
type = "ssh"
user = "root"
private_key = file("kubernetes")
timeout = "2m"
host = vultr_instance.kubernetes_instance[each.key].ipv4_address
}
provisioner "remote-exec" {
inline = "sudo hostnamectl set-hostname ${each.value}"
} */
}
locals {
expanded_names = flatten([
for name, count in var.host_name : [
for i in range(count) : format("%s-%02d", name, i + 1)
]
])
}
provider.tf
terraform {
required_providers {
vultr = {
source = "vultr/vultr"
version = "2.3.1"
}
}
}
provider "vultr" {
api_key = "***************************"
rate_limit = 700
retry_limit = 3
}
variables.tf
variable "host_name" {
type = map(number)
default = {
"Manager" = 1
"Worker" = 2
}
}
The property you are looking for is called main_ip instead of ip4_address or something like that. Specifically accessible via self.main_ip in your connection block.
$ terraform -v
Terraform v0.14.6
I have a Terraform plan that sets up alarms for some of my AWS ECS services, and it looks like this
module "ecs_high_cpu_service_aaa_alarm" {
source = "../modules/cw_alarm"
alarm_name = "ecs-high-cpu-service-aaa-alarm"
service_name = "service-aaa"
// Other parameters
}
module "ecs_high_cpu_service_bbb_alarm" {
source = "../modules/cw_alarm"
alarm_name = "ecs-high-cpu-service-bbb-alarm"
service_name = "service-bbb"
// Other parameters
}
module "ecs_high_cpu_service_123_alarm" {
source = "../modules/cw_alarm"
alarm_name = "ecs-high-cpu-service-123-alarm"
service_name = "service-123"
// Other parameters
}
// More alarms with similar setup as above
As you can see, the modules are all set up similarly, differing only in name, alarm_name and service_name parameters. Is there a way to setup a for-loop that will loop over a map to set the modules up for the plan?
From Mark B's (thanks!) comment, this works.
variables.tf
------------
variable "service_map" {
type = map
default = {
service-aaa = "ecs-high-cpu-service-aaa-alarm"
service-bbb = "ecs-high-cpu-service-bbb-alarm"
service-123 = "ecs-high-cpu-service-123-alarm"
}
description = "Service map; key = service name, value = alarm name"
}
main.tf
-------
module "alarms" {
for_each = var.service_map
source = "../modules/cw_alarm"
service_name = each.key
alarm_name = each.value
// Other parameters
}