Specifying use_ipam_pool = true sets provided cidr to null.
Closed this issue · 7 comments
Description
Try module config as specified in
Terraform module sets cidr to null if use_ipam_pool is true:
This results following error when trying to execute module configured with ipam and given CIDR:
aws_vpc_ipam_preview_next_cidr.this: Creating...
╷
│ Error: allocating cidr from IPAM pool (ipam-pool-01c77e7ea1ea0783d): operation error EC2: AllocateIpamPoolCidr, https response error StatusCode: 400, RequestID: 606540c0-39fe-4e67-8e12-afd98549f252, api error InvalidParameterCombination: The request cannot contain empty CIDR and Netmask Length for a pool with no default allocation netmask length set. Exactly one of them should be provided.
│
│ with aws_vpc_ipam_preview_next_cidr.this,
│ on main.tf line 80, in resource "aws_vpc_ipam_preview_next_cidr" "this":
│ 80: resource "aws_vpc_ipam_preview_next_cidr" "this" {
│
╵
╷
│ Error: creating EC2 VPC: operation error EC2: CreateVpc, https response error StatusCode: 400, RequestID: 42c6d28f-5e47-4f92-9897-c9052ec7000b, api error InvalidParameterCombination: The request cannot contain empty CIDR and Netmask Length for a pool with no default allocation netmask length set. Exactly one of them should be provided.
│
│ with module.vpc_ipam_set_cidr.aws_vpc.this[0],
│ on .terraform/modules/vpc_ipam_set_cidr/main.tf line 28, in resource "aws_vpc" "this":
│ 28: resource "aws_vpc" "this" {
Versions
- Module version [Required]: 5.15.0
- Terraform version: 1.7.5
- Provider version(s): 5.75.0
Reproduction Code [Required]
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.46"
}
}
}
provider "aws" {
region = local.region
}
data "aws_availability_zones" "available" {}
locals {
name = "ex-${basename(path.cwd)}"
region = "eu-west-1"
azs = slice(data.aws_availability_zones.available.names, 0, 3)
preview_partition = cidrsubnets(aws_vpc_ipam_preview_next_cidr.this.cidr, 2, 2, 2)
tags = {
Example = local.name
GithubRepo = "terraform-aws-vpc"
GithubOrg = "terraform-aws-modules"
}
}
################################################################################
# VPC Module
################################################################################
# IPv4
module "vpc_ipam_set_cidr" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0"
name = "${local.name}-set-cidr"
use_ipam_pool = true
ipv4_ipam_pool_id = aws_vpc_ipam_pool.this.id
cidr = "10.1.0.0/16"
azs = local.azs
private_subnets = ["10.1.1.0/24", "10.1.2.0/24", "10.1.3.0/24"]
public_subnets = ["10.1.11.0/24", "10.1.12.0/24", "10.1.13.0/24"]
tags = local.tags
}
resource "aws_vpc_ipam" "this" {
operating_regions {
region_name = local.region
}
tags = local.tags
}
# IPv4
resource "aws_vpc_ipam_pool" "this" {
description = "IPv4 pool"
address_family = "ipv4"
ipam_scope_id = aws_vpc_ipam.this.private_default_scope_id
locale = local.region
tags = local.tags
}
resource "aws_vpc_ipam_pool_cidr" "this" {
ipam_pool_id = aws_vpc_ipam_pool.this.id
cidr = "10.0.0.0/8"
}
resource "aws_vpc_ipam_preview_next_cidr" "this" {
ipam_pool_id = aws_vpc_ipam_pool.this.id
netmask_length = 16
depends_on = [
aws_vpc_ipam_pool_cidr.this
]
}
Steps to reproduce the behavior:
Save terraform code and run
terraform plan -out a.out
terraform apply a.out
Expected behavior
VPC created without a problem if default netmask is not set on IPAM pool. User of this module may instead set netmask length on preview resource.
Actual behavior
Error message received as described above. This really prevents doing CIDR math beforehand because module sets given CIDR to zero. There is no technical limitation or reason on AWS provider side to do so.
Terminal Output Screenshot(s)
Additional context
If one was to remove setting cidr to null, VPC is created successfully!
# cidr_block = var.use_ipam_pool ? null : var.cidr
cidr_block = var.cidr
That's not how IPAM works, IPAM should create the VPC CIDR, not you
Actually it does. Using preview resource. I use this preview resource and take its CIDR and then calculate my subnets, pretty much like in example 1 in your module. But instead of letting VPC now ask for new CIDR, I provide one I got from preview resource.
And this error btw happens in your example too if IPAM pool does not have default subnet mask set. According to response received from AWS API, both approaches seem to be correct, either specifing CIDR that is in valid pool range or by specifying netmask. IPAM checks is CIDR asked is available and if it does, it creates allocation and links it to VPC. Forcing CIDR to null is crippling indented use I think
Btw, awesome speed in answering @bryantbiggs !
Kindly, please let me know if this can be addressed. If not, I might need temporarily fork this repo for my use case.
Siim
you have a couple issues here:
- Your IPAM configurations are not setup correctly, that is the errors that you are seeing. IPAM is unable to preview the next available CIDR and therefore is unable to pass a CIDR to this module
- You cannot set
use_ipam_pool
andcidr_block
- the former tells the module/resources that IPAM will be used to fetch the appropriate CIDR, the latter is just simply a static CIDR that the module has no context as to where it came from.
You first need to sort out your IPAM errors, but even then - don't pass a CIDR to the module when IPAM should be used to generate one. If you fix the first error, you most likely will then run into an error where IPAM generates a CIDR that doesn't contain the subnet CIDRS - this is why our example is all dynamic because the base CIDR is not known until IPAM provides one and then everything is derived off of that CIDR
Thanks for quick reply. I'll try to elaborate:
I provided this TF code as example for quick demonstration of error in question, this is not how I use IPAM here. Dynamic example btw here requires first targeted plan and apply because it fails to get required counts for subnets in module. I wanted to prevent this in code demonstrating actual issue..
In reality, I do get CIDR from IPAM using resource "aws_vpc_ipam_preview_next_cidr"
, abbreviavated code is approx like this:
terraform {
required_version = ">= 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.52"
}
}
}
provider "aws" {
region = var.region
}
variable region {
type = string
default = "eu-central-1"
description = "AWS region"
}
variable name {
type = string
default = "default-vpc"
description = "VPC name"
}
# Subnets info
variable private_subnets_count {
type = number
default = 3
description = "Number of private subnets. Cannot have more private subnets than availability zones. If zero, then NatGW is not created"
}
variable private_subnets_length {
type = number
default = 24
description = "Netmask length of private subnets"
}
variable public_subnets_count {
type = number
default = 3
description = "Number of public subnets"
}
variable public_subnets_length {
type = number
default = 24
description = "Netmask length of public subnets"
}
data "aws_region" "current" {}
data "aws_availability_zones" "available" {
state = "available"
filter {
name = "opt-in-status"
values = ["opt-in-not-required"]
}
}
locals {
region = data.aws_region.current.name
private_subnets_count = length(data.aws_availability_zones.available.names) < var.private_subnets_count ? length(data.aws_availability_zones.available.names) : var.private_subnets_count
vpc_cidr_length = 32 - ceil(log(local.private_subnets_count*pow(2,32-var.private_subnets_length) + var.public_subnets_count*pow(2,32-var.public_subnets_length), 2))
_sn = var.private_subnets_length < var.public_subnets_length ? [
{length = var.private_subnets_length, count = local.private_subnets_count},
{length = var.public_subnets_length, count = var.public_subnets_count}
] : [
{length = var.public_subnets_length, count = var.public_subnets_count},
{length = var.private_subnets_length, count = local.private_subnets_count}
]
_sn_a = [
for ix, config in range(local._sn[0].count):
cidrsubnet(local.vpc_cidr,local._sn[0].length - local.vpc_cidr_length, ix)
]
_sn_b = [
for ix, config in range(local._sn[1].count):
cidrsubnet(
local.vpc_cidr,
local._sn[1].length - local.vpc_cidr_length,
(local._sn[0].count * pow(2,(local._sn[1].length - local._sn[0].length)))+ix
)
]
ipam_pool_description = "o-${local.region}"
vpc_cidr = aws_vpc_ipam_preview_next_cidr.current.cidr
# all_subnets = cidrsubnets(local.vpc_cidr, 2,2,2,2)
private_subnets = var.private_subnets_length < var.public_subnets_length ? local._sn_a : local._sn_b
public_subnets = var.private_subnets_length < var.public_subnets_length ? local._sn_b : local._sn_a
}
data "aws_vpc_ipam_pool" "pool" {
filter {
name = "description"
values = [local.ipam_pool_description]
}
filter {
name = "address-family"
values = ["ipv4"]
}
}
resource "aws_vpc_ipam_preview_next_cidr" "current" {
ipam_pool_id = data.aws_vpc_ipam_pool.pool.id
netmask_length = local.vpc_cidr_length
}
resource "aws_vpc" "this" {
cidr_block = local.vpc_cidr
ipv4_ipam_pool_id = data.aws_vpc_ipam_pool.pool.id
#ipv4_netmask_length = var.ipv4_netmask_length
enable_dns_hostnames = true
enable_dns_support = true
tags = { "Name" = var.name }
}
We have for each region created regional IPAM pool with description o-<region>
, for example o-eu-central-1
and these pools are shared to organization with RAM.
I do not make use of calculated local variables private_subnets and public_subnets in this snippet but they are here to demonstrate why they are here:
They are here for simplifying use of module - operator does not need to know in advance size of cidr it tries to allocate, this is calculated based on number of subnets needed and their size.
This code gets CIDR from IPAM and it gets allocated for VPC just fine, so one can in fact specify CIDR to aws_vpc resource while at same time specifying IPAM pool to use.
My original code in issue description was to demonstrate just the issue I think module has.
Created vpc resource will look like this:
# aws_vpc.this:
resource "aws_vpc" "this" {
arn = "arn:aws:ec2:eu-central-1:<redacted>:vpc/vpc-005bf19926d6379b5"
assign_generated_ipv6_cidr_block = false
cidr_block = "10.72.0.0/24"
default_network_acl_id = "acl-00939733d5dc9f207"
default_route_table_id = "rtb-04f046a49572e9a6b"
default_security_group_id = "sg-08286a47fe9160232"
dhcp_options_id = "dopt-0fad892e7c477b992"
enable_dns_hostnames = true
enable_dns_support = true
enable_network_address_usage_metrics = false
id = "vpc-005bf19926d6379b5"
instance_tenancy = "default"
ipv4_ipam_pool_id = "ipam-pool-05302d90109dbcf48"
ipv6_netmask_length = 0
main_route_table_id = "rtb-04f046a49572e9a6b"
owner_id = "<redacted>"
tags = {
"Name" = "dev-vpc"
}
tags_all = {
"Name" = "dev-vpc"
}
}
Thanks
This issue has been automatically marked as stale because it has been open 30 days
with no activity. Remove stale label or comment or this issue will be closed in 10 days
This issue was automatically closed because of stale in 10 days