Skip to content

Commit

Permalink
Merge pull request #59 from drewmullen/58-multi-public-cidr
Browse files Browse the repository at this point in the history
BREAKING: allow setting multiple public cidrs
  • Loading branch information
andrew-glenn authored Jul 21, 2023
2 parents 991dcf0 + d710a3b commit ef908fc
Show file tree
Hide file tree
Showing 14 changed files with 186 additions and 23 deletions.
5 changes: 4 additions & 1 deletion .header.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Terraform Module for Amazon VPC IP Address Manager on AWS

<i>Note: For information regarding the 2.0 upgrade see our [upgrade guide](https://github.com/aws-ia/terraform-aws-ipam/blob/main/docs/UPGRADE-GUIDE-2.0.md).</i>

This module helps deploy AWS IPAM including IPAM Pools, Provisioned CIDRs, and can help with sharing those pools via AWS RAM.

Built to accommodate a wide range of use cases, this Terraform module can deploy both simple and complex Amazon Virtual Private Cloud (Amazon VPC) IP Address Manager (IPAM) configurations. It supports both symmetrically nested, multi-Region deployments (most common IPAM designs) as well as [asymmetically nested deployments](https://github.com/aws-ia/terraform-aws-ipam/blob/main/images/asymmetrical_example.png).

Refer to the [examples/](https://github.com/aws-ia/terraform-aws-ipam/blob/main/examples) directory in this GitHub repository for examples.
Expand Down Expand Up @@ -60,7 +64,6 @@ variable "pool_config" {
allocation_resource_tags = optional(map(string))
tags = optional(map(string))
cidr_authorization_context = optional(map(string))
sub_pools = optional(any)
})
Expand Down
1 change: 1 addition & 0 deletions .terraform-docs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ settings:
escape: true
html: true
indent: 2
lockfile: false
required: true
sensitive: true
type: true
Expand Down
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
<!-- BEGIN_TF_DOCS -->
# Terraform Module for Amazon VPC IP Address Manager on AWS

<i>Note: For information regarding the 2.0 upgrade see our [upgrade guide](https://github.com/aws-ia/terraform-aws-ipam/blob/main/docs/UPGRADE-GUIDE-2.0.md).</i>

This module helps deploy AWS IPAM including IPAM Pools, Provisioned CIDRs, and can help with sharing those pools via AWS RAM.

Built to accommodate a wide range of use cases, this Terraform module can deploy both simple and complex Amazon Virtual Private Cloud (Amazon VPC) IP Address Manager (IPAM) configurations. It supports both symmetrically nested, multi-Region deployments (most common IPAM designs) as well as [asymmetically nested deployments](https://github.com/aws-ia/terraform-aws-ipam/blob/main/images/asymmetrical_example.png).

Refer to the [examples/](https://github.com/aws-ia/terraform-aws-ipam/blob/main/examples) directory in this GitHub repository for examples.
Expand Down Expand Up @@ -61,7 +65,6 @@ variable "pool_config" {
allocation_resource_tags = optional(map(string))
tags = optional(map(string))
cidr_authorization_context = optional(map(string))
sub_pools = optional(any)
})
Expand Down Expand Up @@ -151,7 +154,7 @@ The IPAM `operating_region` variable must be set for the primary Region in your
| <a name="input_pool_configurations"></a> [pool\_configurations](#input\_pool\_configurations) | A multi-level, nested map describing nested IPAM pools. Can nest up to three levels with the top level being outside the `pool_configurations` in vars prefixed `top_`. If arugument descriptions are omitted, you can find them in the [official documentation](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_ipam_pool#argument-reference).<br><br>- `ram_share_principals` = (optional, list(string)) of valid organization principals to create ram shares to.<br>- `name` = (optional, string) name to give the pool, the key of your map in var.pool\_configurations will be used if omitted.<br>- `description` = (optional, string) description to give the pool, the key of your map in var.pool\_configurations will be used if omitted.<br>- `cidr` = (optional, list(string)) list of CIDRs to provision into pool. Conflicts with `netmask_length`.<br>- `netmask_length` = (optional, number) netmask length to request provisioned into pool. Conflicts with `cidr`.<br><br>- `locale` = (optional, string) locale to set for pool.<br>- `auto_import` = (optional, string)<br>- `tags` = (optional, map(string))<br>- `allocation_default_netmask_length` = (optional, string)<br>- `allocation_max_netmask_length` = (optional, string)<br>- `allocation_min_netmask_length` = (optional, string)<br>- `allocation_resource_tags` = (optional, map(string))<br><br>The following arguments are available but only relevant for public ips<br>- `cidr_authorization_context` = (optional, map(string)) Details found in [official documentation](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_ipam_pool_cidr#cidr_authorization_context).<br>- `aws_service` = (optional, string)<br>- `publicly_advertisable` = (optional, bool)<br><br>- `sub_pools` = (nested repeats of pool\_configuration object above) | `any` | `{}` | no |
| <a name="input_tags"></a> [tags](#input\_tags) | Tags to add to the aws\_vpc\_ipam resource. | `any` | `{}` | no |
| <a name="input_top_auto_import"></a> [top\_auto\_import](#input\_top\_auto\_import) | `auto_import` setting for top-level pool. | `bool` | `null` | no |
| <a name="input_top_cidr_authorization_context"></a> [top\_cidr\_authorization\_context](#input\_top\_cidr\_authorization\_context) | A signed document that proves that you are authorized to bring the specified IP address range to Amazon using BYOIP. Document is not stored in the state file. For more information, refer to https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_ipam_pool_cidr#cidr_authorization_context. | `any` | `null` | no |
| <a name="input_top_cidr_authorization_contexts"></a> [top\_cidr\_authorization\_contexts](#input\_top\_cidr\_authorization\_contexts) | CIDR must match a CIDR defined in `var.top_cidr`. A list of signed documents that proves that you are authorized to bring the specified IP address range to Amazon using BYOIP. Document is not stored in the state file. For more information, refer to https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_ipam_pool_cidr#cidr_authorization_context. | <pre>list(object({<br> cidr = string<br> message = string<br> signature = string<br> }))</pre> | `[]` | no |
| <a name="input_top_description"></a> [top\_description](#input\_top\_description) | Description of top-level pool. | `string` | `""` | no |
| <a name="input_top_name"></a> [top\_name](#input\_top\_name) | Name of top-level pool. | `string` | `null` | no |
| <a name="input_top_netmask_length"></a> [top\_netmask\_length](#input\_top\_netmask\_length) | Top-level netmask length to request. Not possible to use for IPv4. Only possible to use with amazon provided ipv6. | `number` | `null` | no |
Expand Down
34 changes: 34 additions & 0 deletions docs/UPGRADE-GUIDE-2.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Upgrade from v1 to v2

**NOTE: If you are not using public IPs there are no changes required to upgrade to v2.**

In order to support importing multiple public IPs into AWS IPAM, we have updated the variable `top_cidr_authorization_context`. This variable has been renamed to `top_cidr_authorization_contexts` (notice the `s`) which has a strict structure for to inform provision public cidrs into the top level pool.


## Upgrade Guide

### HCL upgrade

Previously you could only specify the context for [1 public ip](https://github.com/aws-ia/terraform-aws-ipam/blob/991dcf02fd2175bd3a6b10a4ee61b01cf89f813d/examples/single_scope_ipv6/main.tf#L15C1-L18C4). This should now be updated to a list of maps that includes the corresponding cidr. See example below


#### Before

```hcl
top_cidr_authorization_context = {
message = var.cidr_authorization_context_message
signature = var.cidr_authorization_context_signature
}
```

#### After

```hcl
top_cidr_authorization_contexts = [{
cidr = var.cidr_authorization_context_cidr
message = var.cidr_authorization_context_message
signature = var.cidr_authorization_context_signature
}]
```

**IMPORTANT: Each `top_cidr_authorization_contexts[#].cidr` must have a corresponding matching reference in the `top_cidr` list.**
54 changes: 54 additions & 0 deletions examples/multiple_scopes/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<!-- BEGIN_TF_DOCS -->
## Multiple Scopes

There are several reasons you may want to populate multiple IPAM scopes:

- Public & Private scope
- IPv4 + IPv6
- Overlapping IPv4 ranges

This example shows you how to build scopes for 2 overlapping IPv4 ranges that you want IPAM to manage. You do this by:

1. invoke module to build IPAM + ipv4 pool\_configuration
2. create a new private scope on the IPAM built in step 1
3. invoke module with `create_ipam = false` and pass in the new scope id created

For IPv4 + IPv6, skip step 2. Reference the `public_default_scope_id` from the ipam in step 1 instead of creating a new scope.

![Multiple Scopes](../../images/multiple\_ipv4\_scopes.png "Multiple Scopes")

## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | = 4.2 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | = 4.2 |

## Modules

| Name | Source | Version |
|------|--------|---------|
| <a name="module_ipv4_scope"></a> [ipv4\_scope](#module\_ipv4\_scope) | ../.. | n/a |
| <a name="module_overlapping_cidr_second_ipv4_scope"></a> [overlapping\_cidr\_second\_ipv4\_scope](#module\_overlapping\_cidr\_second\_ipv4\_scope) | ../.. | n/a |

## Resources

| Name | Type |
|------|------|
| [aws_vpc_ipam_scope.scope_for_overlapping_cidr](https://registry.terraform.io/providers/hashicorp/aws/4.2/docs/resources/vpc_ipam_scope) | resource |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_cidr"></a> [cidr](#input\_cidr) | n/a | `string` | `"10.0.0.0/8"` | no |

## Outputs

No outputs.
<!-- END_TF_DOCS -->
38 changes: 38 additions & 0 deletions examples/single_scope_ipv6/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<!-- BEGIN_TF_DOCS -->
## IPv6 Basic Deployment

The example shows you how to build an IPAM and populate the public scope with IPv6.

![IPv6 Pool structure](../../images/ipv6\_example.png "Region Separated Pools")

## Requirements

No requirements.

## Providers

No providers.

## Modules

| Name | Source | Version |
|------|--------|---------|
| <a name="module_ipv6_basic"></a> [ipv6\_basic](#module\_ipv6\_basic) | ../.. | n/a |

## Resources

No resources.

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_cidr_authorization_context_cidr"></a> [cidr\_authorization\_context\_cidr](#input\_cidr\_authorization\_context\_cidr) | CIDR Authorization Context CIDR. MUST MATCH a cidr in var.ipv6\_cidr | `any` | n/a | yes |
| <a name="input_cidr_authorization_context_message"></a> [cidr\_authorization\_context\_message](#input\_cidr\_authorization\_context\_message) | CIDR Authorization Context Message. | `any` | n/a | yes |
| <a name="input_cidr_authorization_context_signature"></a> [cidr\_authorization\_context\_signature](#input\_cidr\_authorization\_context\_signature) | CIDR Authorization Context Signature. | `any` | n/a | yes |
| <a name="input_ipv6_cidr"></a> [ipv6\_cidr](#input\_ipv6\_cidr) | Top CIDR IPv6. | `any` | n/a | yes |

## Outputs

No outputs.
<!-- END_TF_DOCS -->
5 changes: 3 additions & 2 deletions examples/single_scope_ipv6/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ module "ipv6_basic" {
address_family = "ipv6"
ipam_scope_type = "public"

top_cidr_authorization_context = {
top_cidr_authorization_contexts = [{
cidr = var.cidr_authorization_context_cidr
message = var.cidr_authorization_context_message
signature = var.cidr_authorization_context_signature
}
}]

pool_configurations = {
us-east-1 = {
Expand Down
4 changes: 4 additions & 0 deletions examples/single_scope_ipv6/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ variable "cidr_authorization_context_message" {
variable "cidr_authorization_context_signature" {
description = "CIDR Authorization Context Signature."
}

variable "cidr_authorization_context_cidr" {
description = "CIDR Authorization Context CIDR. MUST MATCH a cidr in var.ipv6_cidr"
}
16 changes: 9 additions & 7 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,16 @@ module "level_zero" {
ipam_scope_id = local.scope_id
source_ipam_pool_id = null

cidr_authorization_contexts = var.top_cidr_authorization_contexts

pool_config = {
cidr = var.top_cidr
ram_share_principals = var.top_ram_share_principals
auto_import = var.top_auto_import
description = var.top_description
cidr_authorization_context = var.top_cidr_authorization_context
name = var.top_name
netmask_length = var.top_netmask_length
cidr = var.top_cidr
ram_share_principals = var.top_ram_share_principals
auto_import = var.top_auto_import
description = var.top_description

name = var.top_name
netmask_length = var.top_netmask_length
}
}

Expand Down
3 changes: 2 additions & 1 deletion modules/sub_pool/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ No modules.
|------|-------------|------|---------|:--------:|
| <a name="input_address_family"></a> [address\_family](#input\_address\_family) | IPv4/6 address family. | `string` | n/a | yes |
| <a name="input_ipam_scope_id"></a> [ipam\_scope\_id](#input\_ipam\_scope\_id) | IPAM Scope ID to attach the pool to. | `string` | n/a | yes |
| <a name="input_pool_config"></a> [pool\_config](#input\_pool\_config) | Configuration of the Pool you want to deploy. All aws\_vpc\_ipam\_pool arguments are available as well as ram\_share\_principals list and sub\_pools map (up to 3 levels). | <pre>object({<br> cidr = optional(list(string))<br> ram_share_principals = optional(list(string))<br><br> locale = optional(string)<br> allocation_default_netmask_length = optional(string)<br> allocation_max_netmask_length = optional(string)<br> allocation_min_netmask_length = optional(string)<br> auto_import = optional(string)<br> aws_service = optional(string)<br> description = optional(string)<br> name = optional(string)<br> netmask_length = optional(number)<br> publicly_advertisable = optional(bool)<br><br> allocation_resource_tags = optional(map(string))<br> tags = optional(map(string))<br> cidr_authorization_context = optional(map(string))<br><br> sub_pools = optional(any)<br> })</pre> | n/a | yes |
| <a name="input_pool_config"></a> [pool\_config](#input\_pool\_config) | Configuration of the Pool you want to deploy. All aws\_vpc\_ipam\_pool arguments are available as well as ram\_share\_principals list and sub\_pools map (up to 3 levels). | <pre>object({<br> cidr = optional(list(string))<br> ram_share_principals = optional(list(string))<br><br> locale = optional(string)<br> allocation_default_netmask_length = optional(string)<br> allocation_max_netmask_length = optional(string)<br> allocation_min_netmask_length = optional(string)<br> auto_import = optional(string)<br> aws_service = optional(string)<br> description = optional(string)<br> name = optional(string)<br> netmask_length = optional(number)<br> publicly_advertisable = optional(bool)<br><br> allocation_resource_tags = optional(map(string))<br> tags = optional(map(string))<br><br> sub_pools = optional(any)<br> })</pre> | n/a | yes |
| <a name="input_source_ipam_pool_id"></a> [source\_ipam\_pool\_id](#input\_source\_ipam\_pool\_id) | IPAM parent pool ID to attach the pool to. | `string` | n/a | yes |
| <a name="input_cidr_authorization_contexts"></a> [cidr\_authorization\_contexts](#input\_cidr\_authorization\_contexts) | A list of signed documents that proves that you are authorized to bring the specified IP address range to Amazon using BYOIP. Document is not stored in the state file. For more information, refer to https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_ipam_pool_cidr#cidr_authorization_context. | <pre>list(object({<br> cidr = string<br> message = string<br> signature = string<br> }))</pre> | `[]` | no |
| <a name="input_implied_description"></a> [implied\_description](#input\_implied\_description) | Description is implied from the pool tree name <parent>/<child> unless specified on the pool\_config. | `string` | `null` | no |
| <a name="input_implied_locale"></a> [implied\_locale](#input\_implied\_locale) | Locale is implied from a parent pool even if another is specified. Its not possible to set child pools to different locales. | `string` | `"None"` | no |
| <a name="input_implied_name"></a> [implied\_name](#input\_implied\_name) | Name is implied from the pool tree name <parent>/<child> unless specified on the pool\_config. | `string` | `null` | no |
Expand Down
12 changes: 9 additions & 3 deletions modules/sub_pool/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ locals {

ram_share_enabled = try(length(var.pool_config.ram_share_principals), 0) > 0
pool_cidrs = var.pool_config.cidr == null ? [var.pool_config.netmask_length] : var.pool_config.cidr
cidr_authorization_contexts = {
for k, v in var.cidr_authorization_contexts : v.cidr => {
message = v.message,
signature = v.signature
}
}
}

resource "aws_vpc_ipam_pool" "sub" {
Expand Down Expand Up @@ -36,10 +42,10 @@ resource "aws_vpc_ipam_pool_cidr" "sub" {
netmask_length = length(regexall("/", each.key)) == 0 ? each.key : null

dynamic "cidr_authorization_context" {
for_each = var.pool_config.cidr_authorization_context == null ? [] : [1]
for_each = length(var.cidr_authorization_contexts) == 0 ? [] : [1]
content {
message = var.pool_config.cidr_authorization_context.message
signature = var.pool_config.cidr_authorization_context.signature
message = local.cidr_authorization_contexts[each.key].message
signature = local.cidr_authorization_contexts[each.key].signature
}
}
}
Expand Down
16 changes: 13 additions & 3 deletions modules/sub_pool/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@ variable "pool_config" {
netmask_length = optional(number)
publicly_advertisable = optional(bool)

allocation_resource_tags = optional(map(string))
tags = optional(map(string))
cidr_authorization_context = optional(map(string))
allocation_resource_tags = optional(map(string))
tags = optional(map(string))

sub_pools = optional(any)
})
Expand All @@ -31,6 +30,17 @@ variable "pool_config" {
}
}

variable "cidr_authorization_contexts" {
description = "A list of signed documents that proves that you are authorized to bring the specified IP address range to Amazon using BYOIP. Document is not stored in the state file. For more information, refer to https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_ipam_pool_cidr#cidr_authorization_context."
type = list(object({
cidr = string
message = string
signature = string
}))
default = []
}


variable "implied_locale" {
description = "Locale is implied from a parent pool even if another is specified. Its not possible to set child pools to different locales."
type = string
Expand Down
1 change: 1 addition & 0 deletions test/examples_single_scope_ipv6_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func TestExamplesIPv6(t *testing.T) {
TerraformDir: "../examples/single_scope_ipv6",
Vars: map[string]interface{}{
"ipv6_cidr": _ipv6_cidr,
"cidr_authorization_context_cidr": _ipv6_cidr,
"cidr_authorization_context_message": _message,
"cidr_authorization_context_signature": _signature,
},
Expand Down
Loading

0 comments on commit ef908fc

Please sign in to comment.