I would like to have addresses like this for subnets within an AWS region:
10.0.0.0/21
10.0.8.0/21
10.0.16.0/21
10.0.24.0/21
10.0.32.0/21
...
Basically, increment the second number by 8.
I also will have a VPN per region, and they should share the same network. So I want them like this:
10.1.0.0/16
10.2.0.0/16
10.3.0.0/16
10.4.0.0/16
10.5.0.0/16
...
Basically incrementing the 3rd number by 1 each time. If not possible to go 1 each step, then 8 as well.
I have read in depth about CIDR blocks, but still don't quite no how to apply, specifically in the following context.
I would like to have a suite of nested Terraform modules that start with 1 CIDR block, and subnet it into smaller and smaller chunks. First, it will subdivide it by region (3rd number), and then by availability zone (second number). Everything will be dynamic, so you only supply the initial CIDR block literally.
It seems that I should be using cidrsubnet:
cidrsubnet(prefix, newbits, netnum)
I got this far sort of:
cidrsubnet(var.cidr_block, 13, netnum?)
That 13 I know will go from /8 to /21. But that goes straight to the availability zone (and I don't know what to do with netnum). I would like to do something like this:
# main.tf
variable "cidr_block" {
default = "10.0.0.0/8"
}
module "region1" {
source = "./region" # ./region/main.tf
cidr_block = cidrsubnet(var.cidr_block, 8?, count.index?) # 10.0.0.0/16?
}
module "region2" {
source = "./region" # ./region/main.tf
cidr_block = cidrsubnet(var.cidr_block, 8?, count.index?) # 10.1.0.0/16?
}
// ...
Then in the region submodule:
# region/main.tf
module "availability_zone1" {
source = "./availability_zone" # ./availability_zone/main.tf
cidr_block = cidrsubnet(var.cidr_block, 5?, count.index?) # 10.1.0.0/21
}
module "availability_zone2" {
source = "./availability_zone" # ./availability_zone/main.tf
cidr_block = cidrsubnet(var.cidr_block, 5?, count.index?) # 10.1.8.0/21
}
module "availability_zone3" {
source = "./availability_zone" # ./availability_zone/main.tf
cidr_block = cidrsubnet(var.cidr_block, 5?, count.index?) # 10.1.16.0/21
}
// ...
So basically, subdividing it once from /8 -> /16, then again from /16 -> /21. The count.index I have not used yet, but somehow I want it to know what position it is in the call sequence. If not possible, that's fine I can just pass an index along with the module.
How do I write this using the cidrsubnet function? The most important part of the question is what I am supposed to put in each slot in the function: cidrsubnet(prefix, newbits, netnum), I don't know specifically what netnum should be like.
At the time I'm writing this answer, Terraform 0.13.0 is at release candidate 1 and due to be released in a few weeks. Terraform 0.13.0 includes the ability to use for_each and count for modules, which makes it more straightforward to use the hashicorp/subnets/cidr module for a multi-level addressing plan like this. An advantage of that module, compared to doing individual cidrsubnet calls directly, is that it allows you to assign symbolic names to each of your networks and it has some conventions to help with deprecating and later reusing parts of your address space without reassigning all of the neighboring networks.
You mentioned AWS regions and subnets in your example so for the sake of showing this I'm going to use AWS region names and availability zones as the symbolic names for the networks, but you can use whatever names make sense for your architecture as long as each network ends up with a unique identifier.
locals {
aws_region_zones = tolist([
{
region = "us-west-2"
zones = ["us-west-2a", "us-west-2b"]
},
{
region = "us-east-1"
zones = ["us-east-1a", "us-east-2f"]
},
# When allocating new regions or zones,
# always add them at the end of their
# respective list to avoid renumbering
# the existing address allocations.
])
}
module "regional" {
source = "hashicorp/subnets/cidr"
base_cidr_block = "10.0.0.0/8"
networks = [
for regional in local.aws_region_zones : {
name = regional.region
new_bits = 8
}
]
}
module "zonal" {
source = "hashicorp/subnets/cidr"
for_each = {
for net in module.regional.networks : net.name => net
}
base_cidr_block = each.value.cidr_block
networks = [
for zone_name in local.aws_region_zones[each.key].zones : {
name = regional.region
new_bits = 5
}
]
}
output "region_cidr_blocks" {
value = tomap({
for net in module.regional.networks : net.name => {
cidr_block = net.cidr_block
zones = tomap({
for subnet in module.zonal[net.name].networks : subnet.name => {
cidr_block = net.cidr_block
}
})
}
})
}
The region_cidr_blocks output value will be a map of objects where each object represents a region, and then a nested map for each availability zone.
What you already have is correct. The count.index would be just incremented by 1.
You can calculated and validate the blocks as follows (terraform 0.12):
provider "aws" {
region = "us-east-1"
}
variable "cidr_block" {
default = "10.0.0.0/8"
}
output "cidr1" {
value = [for index in range(8):
cidrsubnet(var.cidr_block, 8, index)]
}
output "cidr2" {
value = [for index in range(8):
cidrsubnet(var.cidr_block, 13, index)]
}
These will output:
cidr1 = [
"10.0.0.0/16",
"10.1.0.0/16",
"10.2.0.0/16",
"10.3.0.0/16",
"10.4.0.0/16",
"10.5.0.0/16",
"10.6.0.0/16",
"10.7.0.0/16",
]
cidr2 = [
"10.0.0.0/21",
"10.0.8.0/21",
"10.0.16.0/21",
"10.0.24.0/21",
"10.0.32.0/21",
"10.0.40.0/21",
"10.0.48.0/21",
"10.0.56.0/21",
]
To check out all the combinations, you can do:
provider "aws" {
region = "us-east-1"
}
variable "cidr_block" {
default = "10.0.0.0/8"
}
output "all_cidrs" {
value = [for index1 in range(8):
{
cidrsubnet(var.cidr_block, 8, index1) = [
for index2 in range(8): cidrsubnet(cidrsubnet(var.cidr_block, 8, index1), 5, index2)
]
}
]
}
Which gives:
all_cidrs = [
{
"10.0.0.0/16" = [
"10.0.0.0/21",
"10.0.8.0/21",
"10.0.16.0/21",
"10.0.24.0/21",
"10.0.32.0/21",
"10.0.40.0/21",
"10.0.48.0/21",
"10.0.56.0/21",
]
},
{
"10.1.0.0/16" = [
"10.1.0.0/21",
"10.1.8.0/21",
"10.1.16.0/21",
"10.1.24.0/21",
"10.1.32.0/21",
"10.1.40.0/21",
"10.1.48.0/21",
"10.1.56.0/21",
]
},
{
"10.2.0.0/16" = [
"10.2.0.0/21",
"10.2.8.0/21",
"10.2.16.0/21",
"10.2.24.0/21",
"10.2.32.0/21",
"10.2.40.0/21",
"10.2.48.0/21",
"10.2.56.0/21",
]
},
{
"10.3.0.0/16" = [
"10.3.0.0/21",
"10.3.8.0/21",
"10.3.16.0/21",
"10.3.24.0/21",
"10.3.32.0/21",
"10.3.40.0/21",
"10.3.48.0/21",
"10.3.56.0/21",
]
},
{
"10.4.0.0/16" = [
"10.4.0.0/21",
"10.4.8.0/21",
"10.4.16.0/21",
"10.4.24.0/21",
"10.4.32.0/21",
"10.4.40.0/21",
"10.4.48.0/21",
"10.4.56.0/21",
]
},
{
"10.5.0.0/16" = [
"10.5.0.0/21",
"10.5.8.0/21",
"10.5.16.0/21",
"10.5.24.0/21",
"10.5.32.0/21",
"10.5.40.0/21",
"10.5.48.0/21",
"10.5.56.0/21",
]
},
{
"10.6.0.0/16" = [
"10.6.0.0/21",
"10.6.8.0/21",
"10.6.16.0/21",
"10.6.24.0/21",
"10.6.32.0/21",
"10.6.40.0/21",
"10.6.48.0/21",
"10.6.56.0/21",
]
},
{
"10.7.0.0/16" = [
"10.7.0.0/21",
"10.7.8.0/21",
"10.7.16.0/21",
"10.7.24.0/21",
"10.7.32.0/21",
"10.7.40.0/21",
"10.7.48.0/21",
"10.7.56.0/21",
]
},
]
Related
Sorry I am a beginner at terraform and found some useful modules.
I need to make a single aurora instance cluster for non-production and I need to shutdown after business hours.
how can I create an aurora cluster and schedule to run during business hours???
I can't get the scheduler to connect to the RDS cluster
provider "aws" {
region = local.region
}
locals {
name = "example-aurora"
region = "us-east-1"
tags = {
Owner = "user"
Environment = "dev"
}
}
################################################################################
# Supporting Resources
################################################################################
resource "random_password" "master" {
length = 10
}
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 3.0"
name = "aurora_vpc"
cidr = "10.99.0.0/18"
enable_dns_support = true
enable_dns_hostnames = true
azs = ["${local.region}a", "${local.region}b", "${local.region}c"]
public_subnets = ["10.99.0.0/24", "10.99.1.0/24", "10.99.2.0/24"]
private_subnets = ["10.99.3.0/24", "10.99.4.0/24", "10.99.5.0/24"]
database_subnets = ["10.99.7.0/24", "10.99.8.0/24", "10.99.9.0/24"]
tags = local.tags
}
resource "aws_db_parameter_group" "muffy-pg" {
family = "postgres13"
name = "peter-rds-param-group"
parameter {
apply_method = "immediate"
name = "autovacuum_naptime"
value = "30"
}
parameter {
apply_method = "pending-reboot"
name = "autovacuum_max_workers"
value = "15"
}
}
resource "aws_docdb_cluster_parameter_group" "muffy-cluster-pg" {
name = "peter-rds-param-group"
family = "postgres13"
}
module "cluster" {
source = "terraform-aws-modules/rds-aurora/aws"
name = "test-aurora-db-postgres96"
engine = "aurora-postgresql"
engine_version = "13.7"
instance_class = "db.t3.small"
instances = {
one = {}
two = {}
}
vpc_id = module.vpc.vpc_id
subnets = [module.vpc.database_subnets[0], module.vpc.database_subnets[1], module.vpc.database_subnets[2]]
# allowed_security_groups = ["sg-12345678"]
allowed_cidr_blocks = ["10.99.0.0/18"]
storage_encrypted = true
apply_immediately = true
monitoring_interval = 10
db_parameter_group_name = aws_db_parameter_group.muffy-pg.name
db_cluster_parameter_group_name = aws_docdb_cluster_parameter_group.muffy-cluster-pg.name
enabled_cloudwatch_logs_exports = ["postgresql"]
tags = {
Environment = "dev"
Terraform = "true"
}
}
variable "environment" {
default = "dev"
}
module "rds_schedule" {
depends_on = [module.cluster]
source = "github.com/barryw/terraform-aws-rds-scheduler"
# version = "~> 2.0.0"
/* Don't stop RDS in production! */
skip_execution = var.environment == "prod"
identifier = "peter-scheduler"
/* Start the RDS cluster at 6:50am EDT Monday - Friday */
up_schedule = "cron(50 10 ? * MON-FRI *)"
/* Stop the RDS cluster at 9pm EDT every night */
down_schedule = "cron(0 1 * * ? *)"
rds_identifier = module.cluster.identifier
is_cluster = true
}
I guess the issue is with rds_identifier value used under module "rds_schedule"
rds_identifier = module.cluster.identifier
It should be,
rds_identifier = module.cluster.cluster_id
The source module for the aurora cluster used here "terraform-aws-modules/rds-aurora/aws" outputs the cluster identifier as cluster_id not cluster_identifier
Github Reference:
https://registry.terraform.io/modules/terraform-aws-modules/rds-aurora/aws/latest#outputs
Hi my terraform code is here
main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.0"
}
}
}
# Configure the AWS Provider
provider "aws" {
region = var.region
}
# DATA
data "aws_availability_zones" "available" {}
data "template_file" "public_cidrsubnet" {
count = var.subnet_count
template = "$${cidrsubnet(vpc_cidr,8,current_count)}"
vars = {
vpc_cidr = var.network_address_space
current_count = count.index
}
}
resource "aws_vpc" "tf-aws-vn" {
cidr_block = var.network_address_space
tags = local.common_tags
#name = var.name
}
resource "aws_subnet" "tf-aws-sn" {
count = length(data.aws_availability_zones.available.names)
vpc_id = aws_vpc.tf-aws-vn.id
cidr_block = [data.template_file.public_cidrsubnet[*].rendered]
availability_zone = slice(data.aws_availability_zones.available.names, 0, var.subnet_count)
tags = local.common_tags
}
variables.tf
variable "region" {
default = "us-east-1"
}
variable network_address_space {}
variable name {
type = string
}
variable "subnet_count" {
type = number
}
And finally!
terraform.tfvars
network_address_space = "10.0.0.0/16"
subnet_count = 2
I'm getting error like below:
Error: Incorrect attribute value type
on main.tf line 36, in resource "aws_subnet" "tf-aws-sn":
36: cidr_block = [data.template_file.public_cidrsubnet[*].rendered]
Inappropriate value for attribute "cidr_block": string required.
What is the issue?
I want to create n subnets for any address range I provide
My terraform version is 0.13.5
You are passing an array to the cidr_block which results in the given error. You need to pass a string to the cidr block.
cidr_block = data.template_file.public_cidrsubnet[count.index].rendered
You also need to change the * to the actual count. Otherwise you will get an error complaining that it is a tuple with 2 elements.
With your code the result of data.template_file.public_cidrsubnet[*].rendered is
cidr_output = [
"10.0.0.0/24",
"10.0.1.0/24",
]
Then the next issue you will face is with the count itself. You are using the total number of availability zones as count, but you want the count to be the subnet_count. You only have 2 subnets created in the public_cidrsubnet template, so you can't create a subnet in each availability zone.
Then you also need to pass the count.index to the slice for the availability_zone argument.
The correct aws_subnet resource that should work
resource "aws_subnet" "tf-aws-sn" {
count = var.subnet_count
vpc_id = aws_vpc.tf-aws-vn.id
cidr_block = data.template_file.public_cidrsubnet[count.index].rendered
availability_zone = slice(data.aws_availability_zones.available.names, 0, var.subnet_count)[count.index]
}
Is there any way where count.index can be run for some index values and not for all the values in list.
I have a list variable
variable "subnets_cidr" {
type = list
default = ["172.17.1.0/24", "172.17.2.0/24", "172.17.3.0/24","172.17.10.0/24","172.17.11.0/24","172.17.12.0/24"]
}
and a subnet
# Subnets : Public
resource "aws_subnet" "public" {
count = length(var.subnets_cidr)
vpc_id = aws_vpc.terra_vpc.id
cidr_block = element(var.subnets_cidr,count.index)
availability_zone = element(var.azs,count.index)
map_public_ip_on_launch = true
tags = {
Name = element(var.pub_sub_names, count.index)
}
}
With the above code 6 subnets are being created, but i dont want that,i want to limit to create only 3 subnets.
I was thinking to slice the count.index variable like count.index[1:3] but it not working.
also with the below code for route table association, it associating all the subnets but here also i would like to limit the number of subnets. for e.g say first 3 subnets.
# Route table association with public subnets
resource "aws_route_table_association" "a" {
count = length(var.subnets_cidr)
subnet_id = element(aws_subnet.public.*.id,count.index)
route_table_id = aws_route_table.public_rt.id
}
Rather than using count, you can use locals for different subnets
use subnetone for one range of subnets and subnettwo for other subnet ranges
locals {
teams = {
"subnetone" = {
subnet1 = "172.17.1.0/24"
subnet2 = "172.17.2.0/24"
subnet3 = "172.17.3.0/24"
},
"subnettwo" = {
subnet4 = "172.17.10.0/24"
subnet5 = "172.17.11.0/24"
subnet6 = "172.17.12.0/24"
}
}
}
resource "aws_subnet" "public" {
for_each = local.teams.subnetone
cidr_block = each.value
vpc_id = "vpc-09c37360e8b599296"
}
I'm trying to get tf 0.12.x new dynamic feature to work with a nested map, config is below.
As you can see below (simplified for this) I'm defining all the variables and adding variable required_resource_access which contains a map.
I was hoping to use new dynamic feature to create read this map in a nested dyanmic block.
variable prefix {
description = "Prefix to applied to all top level resources"
default = "abx"
}
variable suffix {
description = "Suffix to applied to all valid top level resources, usually this is 2 letter region code such as we (westeurope), ne (northeurope)."
default = "we"
}
variable env {
description = "3 letter environment code appied to all top level resources"
default = "dev"
}
variable location {
description = "Where to create all resources in Azure"
default = "westeurope"
}
variable available_to_other_tenants {
default = false
}
variable oauth2_allow_implicit_flow {
default = true
}
variable public_client {
default = false
}
# other option is native
variable application_type {
default = "webapp/api"
}
variable required_resource_access {
type = list(object({
resource_app_id = string
resource_access = object({
id = string
type = string
})
}))
default = [{
resource_app_id = "00000003-0000-0000-c000-000000000000"
resource_access = {
id = "7ab1d382-f21e-4acd-a863-ba3e13f7da61"
type = "Role"
}
}]
}
variable reply_urls {
default = []
}
variable group_membership_claims {
default = "All"
}
resource "azuread_application" "bootstrap" {
name = "${var.prefix}-${var.env}-spn"
homepage = "http://${var.prefix}-${var.env}-spn"
identifier_uris = ["http://${var.prefix}-${var.env}-spn"]
reply_urls = var.reply_urls
available_to_other_tenants = var.available_to_other_tenants
oauth2_allow_implicit_flow = var.oauth2_allow_implicit_flow
type = var.application_type
group_membership_claims = var.group_membership_claims
dynamic "required_resource_access" {
for_each = var.required_resource_access
content {
resource_app_id = required_resource_access.value["resource_app_id"]
dynamic "resource_access" {
for_each = required_resource_access.value["resource_access"]
content {
id = resource_access.value["id"]
type = resource_access.value["type"]
}
}
}
}
}
But for reasons beyond my knowledge it keeps giving me this error (notice it's priting it twice as well), I've tried a few other options but this is the closest I managed to get where it would at least give me a meaningful error.
------------------------------------------------------------------------
Error: Invalid index
on pe_kubernetes.tf line 24, in resource "azuread_application" "bootstrap":
24: id = resource_access.value["id"]
|----------------
| resource_access.value is "7ab1d382-f21e-4acd-a863-ba3e13f7da61"
This value does not have any indices.
Error: Invalid index
on pe_kubernetes.tf line 24, in resource "azuread_application" "bootstrap":
24: id = resource_access.value["id"]
|----------------
| resource_access.value is "Role"
This value does not have any indices.
Error: Invalid index
on pe_kubernetes.tf line 25, in resource "azuread_application" "bootstrap":
25: type = resource_access.value["type"]
|----------------
| resource_access.value is "7ab1d382-f21e-4acd-a863-ba3e13f7da61"
This value does not have any indices.
Error: Invalid index
on pe_kubernetes.tf line 25, in resource "azuread_application" "bootstrap":
25: type = resource_access.value["type"]
|----------------
| resource_access.value is "Role"
This value does not have any indices.
Spent the best part of 2 days on this with no luck so any help or pointers would be much appreciated!
I had some time to test my comment...
If I change the resource_access to a list it works.
See code below:
variable required_resource_access {
type = list(object({
resource_app_id = string
resource_access = list(object({
id = string
type = string
}))
}))
default = [{
resource_app_id = "00000003-0000-0000-c000-000000000000"
resource_access = [{
id = "7ab1d382-f21e-4acd-a863-ba3e13f7da61"
type = "Role"
}]
}]
}
resource "azuread_application" "bootstrap" {
name = "test"
type = "webapp/api"
group_membership_claims = "All"
dynamic "required_resource_access" {
for_each = var.required_resource_access
content {
resource_app_id = required_resource_access.value["resource_app_id"]
dynamic "resource_access" {
for_each = required_resource_access.value["resource_access"]
content {
id = resource_access.value["id"]
type = resource_access.value["type"]
}
}
}
}
}
And the plan shows:
Terraform will perform the following actions:
# azuread_application.bootstrap will be created
+ resource "azuread_application" "bootstrap" {
+ application_id = (known after apply)
+ available_to_other_tenants = false
+ group_membership_claims = "All"
+ homepage = (known after apply)
+ id = (known after apply)
+ identifier_uris = (known after apply)
+ name = "test"
+ oauth2_allow_implicit_flow = true
+ object_id = (known after apply)
+ owners = (known after apply)
+ public_client = (known after apply)
+ reply_urls = (known after apply)
+ type = "webapp/api"
+ oauth2_permissions {
+ admin_consent_description = (known after apply)
...
}
+ required_resource_access {
+ resource_app_id = "00000003-0000-0000-c000-000000000000"
+ resource_access {
+ id = "7ab1d382-f21e-4acd-a863-ba3e13f7da61"
+ type = "Role"
}
}
}
Plan: 1 to add, 0 to change, 0 to destroy.
I removed a lot of your variables an some of the optional Arguments for azuread_application to keep the code as small as possible, but the same principle applies to your code, use lists on for_each or it will loop on the object properties.
I am trying to create MSK_AWS cluster with terraform. With new released version of terraform, AWS_MSK cluster is not getting created.
Here is the code used below
resource "aws_msk_cluster" "msk_cluster" {
cluster_name = "Testing_Cluster"
kafka_version = "2.1.0"
number_of_broker_nodes = 3
broker_node_group_info {
instance_type = "kafka.m5.large"
client_subnets = [
"${aws_subnet.subnet_a.id}",
"${aws_subnet.subnet_b.id}",
"${aws_subnet.subnet_c.id}",
]
ebs_volume_size = 5
security_groups = [ "${aws_security_group.MSK_Sg.id}" ]
}
tags = {
Name = "Cluster_MSK"
}
}
But I am getting the error
BadRequestException: The parameter value contains one or more
characters that are not valid.status code: 400, request id:
e2589e6a-8161-11e9-8f31-6f8877605e30.
MSK doesn't support underscores in the aws_msk_cluster.cluster_name and aws_msk_configuration.name parameters.
In your case simply use cluster_name = "Testing-Cluster".