diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 7eaa782be..59cd0a896 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -7,30 +7,30 @@ on: - master jobs: -# Min Terraform version(s) + # Min Terraform version(s) getDirectories: - name: Get root directories - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Install Python - uses: actions/setup-python@v2 - - name: Build matrix - id: matrix - run: | - DIRS=$(python -c "import json; import glob; print(json.dumps([x.replace('/versions.tf', '') for x in glob.glob('./**/versions.tf', recursive=True)]))") - echo "::set-output name=directories::$DIRS" - outputs: - directories: ${{ steps.matrix.outputs.directories }} + name: Get root directories + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Install Python + uses: actions/setup-python@v2 + - name: Build matrix + id: matrix + run: | + DIRS=$(python -c "import json; import glob; print(json.dumps([x.replace('/versions.tf', '') for x in glob.glob('./**/versions.tf', recursive=True)]))") + echo "::set-output name=directories::$DIRS" + outputs: + directories: ${{ steps.matrix.outputs.directories }} preCommitMinVersions: name: Min TF validate needs: getDirectories runs-on: ubuntu-latest strategy: - matrix: - directory: ${{ fromJson(needs.getDirectories.outputs.directories) }} + matrix: + directory: ${{ fromJson(needs.getDirectories.outputs.directories) }} steps: - name: Checkout uses: actions/checkout@v2 @@ -59,7 +59,7 @@ jobs: pre-commit run terraform_validate --color=always --show-diff-on-failure --files $(ls *.tf) -# Max Terraform version + # Max Terraform version getBaseVersion: name: Module max TF version runs-on: ubuntu-latest @@ -94,7 +94,7 @@ jobs: - name: Install pre-commit dependencies run: | pip install pre-commit - curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/latest | grep -o -E "https://.+?-v0.12.0-linux-amd64" | head -n1)" > terraform-docs && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/ + curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/latest | grep -o -E "https://.+?-v0.12\..+?-linux-amd64" | head -n1)" > terraform-docs && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/ curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && sudo mv tflint /usr/bin/ - name: Execute pre-commit # Run all pre-commit checks on max version supported diff --git a/README.md b/README.md index 14824c31f..2b16892c5 100644 --- a/README.md +++ b/README.md @@ -220,6 +220,7 @@ It is possible to integrate this VPC module with [terraform-aws-transit-gateway * [VPC with IPv6 enabled](https://github.com/terraform-aws-modules/terraform-aws-vpc/tree/master/examples/ipv6) * [Network ACL](https://github.com/terraform-aws-modules/terraform-aws-vpc/tree/master/examples/network-acls) * [VPC Flow Logs](https://github.com/terraform-aws-modules/terraform-aws-vpc/tree/master/examples/vpc-flow-logs) +* [VPC with Outpost](https://github.com/terraform-aws-modules/terraform-aws-vpc/tree/master/examples/outpost) * [Manage Default VPC](https://github.com/terraform-aws-modules/terraform-aws-vpc/tree/master/examples/manage-default-vpc) * Few tests and edge cases examples: [#46](https://github.com/terraform-aws-modules/terraform-aws-vpc/tree/master/examples/issue-46-no-private-subnets), [#44](https://github.com/terraform-aws-modules/terraform-aws-vpc/tree/master/examples/issue-44-asymmetric-private-subnets), [#108](https://github.com/terraform-aws-modules/terraform-aws-vpc/tree/master/examples/issue-108-route-already-exists) @@ -264,6 +265,7 @@ No modules. | [aws_network_acl.database](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_acl) | resource | | [aws_network_acl.elasticache](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_acl) | resource | | [aws_network_acl.intra](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_acl) | resource | +| [aws_network_acl.outpost](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_acl) | resource | | [aws_network_acl.private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_acl) | resource | | [aws_network_acl.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_acl) | resource | | [aws_network_acl.redshift](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_acl) | resource | @@ -273,6 +275,8 @@ No modules. | [aws_network_acl_rule.elasticache_outbound](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_acl_rule) | resource | | [aws_network_acl_rule.intra_inbound](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_acl_rule) | resource | | [aws_network_acl_rule.intra_outbound](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_acl_rule) | resource | +| [aws_network_acl_rule.outpost_inbound](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_acl_rule) | resource | +| [aws_network_acl_rule.outpost_outbound](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_acl_rule) | resource | | [aws_network_acl_rule.private_inbound](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_acl_rule) | resource | | [aws_network_acl_rule.private_outbound](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_acl_rule) | resource | | [aws_network_acl_rule.public_inbound](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_acl_rule) | resource | @@ -296,6 +300,7 @@ No modules. | [aws_route_table_association.database](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource | | [aws_route_table_association.elasticache](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource | | [aws_route_table_association.intra](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource | +| [aws_route_table_association.outpost](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource | | [aws_route_table_association.private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource | | [aws_route_table_association.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource | | [aws_route_table_association.redshift](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource | @@ -303,6 +308,7 @@ No modules. | [aws_subnet.database](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource | | [aws_subnet.elasticache](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource | | [aws_subnet.intra](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource | +| [aws_subnet.outpost](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource | | [aws_subnet.private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource | | [aws_subnet.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource | | [aws_subnet.redshift](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource | @@ -799,6 +805,17 @@ No modules. | [nat\_eip\_tags](#input\_nat\_eip\_tags) | Additional tags for the NAT EIP | `map(string)` | `{}` | no | | [nat\_gateway\_tags](#input\_nat\_gateway\_tags) | Additional tags for the NAT gateways | `map(string)` | `{}` | no | | [one\_nat\_gateway\_per\_az](#input\_one\_nat\_gateway\_per\_az) | Should be true if you want only one NAT Gateway per availability zone. Requires `var.azs` to be set, and the number of `public_subnets` created to be greater than or equal to the number of availability zones specified in `var.azs`. | `bool` | `false` | no | +| [outpost\_acl\_tags](#input\_outpost\_acl\_tags) | Additional tags for the outpost subnets network ACL | `map(string)` | `{}` | no | +| [outpost\_arn](#input\_outpost\_arn) | ARN of Outpost you want to create a subnet in. | `string` | `null` | no | +| [outpost\_az](#input\_outpost\_az) | AZ where Outpost is anchored. | `string` | `null` | no | +| [outpost\_dedicated\_network\_acl](#input\_outpost\_dedicated\_network\_acl) | Whether to use dedicated network ACL (not default) and custom rules for outpost subnets | `bool` | `false` | no | +| [outpost\_inbound\_acl\_rules](#input\_outpost\_inbound\_acl\_rules) | Outpost subnets inbound network ACLs | `list(map(string))` |
[
{
"cidr_block": "0.0.0.0/0",
"from_port": 0,
"protocol": "-1",
"rule_action": "allow",
"rule_number": 100,
"to_port": 0
}
]
| no | +| [outpost\_outbound\_acl\_rules](#input\_outpost\_outbound\_acl\_rules) | Outpost subnets outbound network ACLs | `list(map(string))` |
[
{
"cidr_block": "0.0.0.0/0",
"from_port": 0,
"protocol": "-1",
"rule_action": "allow",
"rule_number": 100,
"to_port": 0
}
]
| no | +| [outpost\_subnet\_assign\_ipv6\_address\_on\_creation](#input\_outpost\_subnet\_assign\_ipv6\_address\_on\_creation) | Assign IPv6 address on outpost subnet, must be disabled to change IPv6 CIDRs. This is the IPv6 equivalent of map\_public\_ip\_on\_launch | `bool` | `null` | no | +| [outpost\_subnet\_ipv6\_prefixes](#input\_outpost\_subnet\_ipv6\_prefixes) | Assigns IPv6 outpost subnet id based on the Amazon provided /56 prefix base 10 integer (0-256). Must be of equal length to the corresponding IPv4 subnet list | `list(string)` | `[]` | no | +| [outpost\_subnet\_suffix](#input\_outpost\_subnet\_suffix) | Suffix to append to outpost subnets name | `string` | `"outpost"` | no | +| [outpost\_subnet\_tags](#input\_outpost\_subnet\_tags) | Additional tags for the outpost subnets | `map(string)` | `{}` | no | +| [outpost\_subnets](#input\_outpost\_subnets) | A list of outpost subnets inside the VPC | `list(string)` | `[]` | no | | [private\_acl\_tags](#input\_private\_acl\_tags) | Additional tags for the private subnets network ACL | `map(string)` | `{}` | no | | [private\_dedicated\_network\_acl](#input\_private\_dedicated\_network\_acl) | Whether to use dedicated network ACL (not default) and custom rules for private subnets | `bool` | `false` | no | | [private\_inbound\_acl\_rules](#input\_private\_inbound\_acl\_rules) | Private subnets inbound network ACLs | `list(map(string))` |
[
{
"cidr_block": "0.0.0.0/0",
"from_port": 0,
"protocol": "-1",
"rule_action": "allow",
"rule_number": 100,
"to_port": 0
}
]
| no | @@ -982,6 +999,12 @@ No modules. | [nat\_ids](#output\_nat\_ids) | List of allocation ID of Elastic IPs created for AWS NAT Gateway | | [nat\_public\_ips](#output\_nat\_public\_ips) | List of public Elastic IPs created for AWS NAT Gateway | | [natgw\_ids](#output\_natgw\_ids) | List of NAT Gateway IDs | +| [outpost\_network\_acl\_arn](#output\_outpost\_network\_acl\_arn) | ARN of the outpost network ACL | +| [outpost\_network\_acl\_id](#output\_outpost\_network\_acl\_id) | ID of the outpost network ACL | +| [outpost\_subnet\_arns](#output\_outpost\_subnet\_arns) | List of ARNs of outpost subnets | +| [outpost\_subnets](#output\_outpost\_subnets) | List of IDs of outpost subnets | +| [outpost\_subnets\_cidr\_blocks](#output\_outpost\_subnets\_cidr\_blocks) | List of cidr\_blocks of outpost subnets | +| [outpost\_subnets\_ipv6\_cidr\_blocks](#output\_outpost\_subnets\_ipv6\_cidr\_blocks) | List of IPv6 cidr\_blocks of outpost subnets in an IPv6 enabled VPC | | [private\_ipv6\_egress\_route\_ids](#output\_private\_ipv6\_egress\_route\_ids) | List of IDs of the ipv6 egress route. | | [private\_nat\_gateway\_route\_ids](#output\_private\_nat\_gateway\_route\_ids) | List of IDs of the private nat gateway route. | | [private\_network\_acl\_arn](#output\_private\_network\_acl\_arn) | ARN of the private network ACL | diff --git a/examples/outpost/README.md b/examples/outpost/README.md new file mode 100644 index 000000000..2c6200d90 --- /dev/null +++ b/examples/outpost/README.md @@ -0,0 +1,63 @@ +# VPC with Outpost Subnet + +Configuration in this directory creates a VPC with public, private, and private outpost subnets. + +This configuration uses data-source to find an available Outpost by name. Change it according to your needs in order to run this example. + +[Read more about AWS regions, availability zones and local zones](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-regions-availability-zones). + +## Usage + +To run this example you need to execute: + +```bash +$ terraform init +$ terraform plan +$ terraform apply +``` + +Note that this example may create resources which can cost money (AWS Elastic IP, for example). Run `terraform destroy` when you don't need these resources. + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 0.12.21 | +| [aws](#requirement\_aws) | >= 3.5.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 3.5.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [vpc](#module\_vpc) | ../../ | | + +## Resources + +| Name | Type | +|------|------| +| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | +| [aws_outposts_outpost.shared](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/outposts_outpost) | data source | + +## Inputs + +No inputs. + +## Outputs + +| Name | Description | +|------|-------------| +| [azs](#output\_azs) | A list of availability zones specified as argument to this module | +| [nat\_public\_ips](#output\_nat\_public\_ips) | List of public Elastic IPs created for AWS NAT Gateway | +| [outpost\_subnets](#output\_outpost\_subnets) | List of IDs of private subnets | +| [private\_subnets](#output\_private\_subnets) | List of IDs of private subnets | +| [public\_subnets](#output\_public\_subnets) | List of IDs of public subnets | +| [vpc\_cidr\_block](#output\_vpc\_cidr\_block) | The CIDR block of the VPC | +| [vpc\_id](#output\_vpc\_id) | The ID of the VPC | + diff --git a/examples/outpost/main.tf b/examples/outpost/main.tf new file mode 100644 index 000000000..49bb96735 --- /dev/null +++ b/examples/outpost/main.tf @@ -0,0 +1,150 @@ +provider "aws" { + region = "us-west-2" + + assume_role { + role_arn = "arn:aws:iam::562806027032:role/outpost-shared-anton" + } +} + +data "aws_outposts_outpost" "shared" { + name = "SEA19.07" +} + +data "aws_availability_zones" "available" {} + +module "vpc" { + source = "../../" + + name = "outpost-example" + + cidr = "10.0.0.0/16" + + azs = [ + data.aws_availability_zones.available.names[0], + data.aws_availability_zones.available.names[1], + data.aws_availability_zones.available.names[2], + ] + private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"] + public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"] + + # Outpost is using single AZ specified in `outpost_az` + outpost_subnets = ["10.0.50.0/24", "10.0.51.0/24"] + outpost_arn = data.aws_outposts_outpost.shared.arn + outpost_az = data.aws_outposts_outpost.shared.availability_zone + + # IPv6 + enable_ipv6 = true + outpost_subnet_assign_ipv6_address_on_creation = true + outpost_subnet_ipv6_prefixes = [2, 3, 4] + + # NAT Gateway + enable_nat_gateway = true + single_nat_gateway = true + + # Network ACLs + outpost_dedicated_network_acl = true + outpost_inbound_acl_rules = local.network_acls["outpost_inbound"] + outpost_outbound_acl_rules = local.network_acls["outpost_outbound"] + + tags = { + Owner = "user" + Environment = "dev" + } +} + +locals { + network_acls = { + outpost_inbound = [ + { + rule_number = 100 + rule_action = "allow" + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_block = "0.0.0.0/0" + }, + { + rule_number = 110 + rule_action = "allow" + from_port = 443 + to_port = 443 + protocol = "tcp" + cidr_block = "0.0.0.0/0" + }, + { + rule_number = 120 + rule_action = "allow" + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_block = "0.0.0.0/0" + }, + { + rule_number = 130 + rule_action = "allow" + from_port = 3389 + to_port = 3389 + protocol = "tcp" + cidr_block = "0.0.0.0/0" + }, + { + rule_number = 140 + rule_action = "allow" + from_port = 80 + to_port = 80 + protocol = "tcp" + ipv6_cidr_block = "::/0" + }, + ] + outpost_outbound = [ + { + rule_number = 100 + rule_action = "allow" + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_block = "0.0.0.0/0" + }, + { + rule_number = 110 + rule_action = "allow" + from_port = 443 + to_port = 443 + protocol = "tcp" + cidr_block = "0.0.0.0/0" + }, + { + rule_number = 120 + rule_action = "allow" + from_port = 1433 + to_port = 1433 + protocol = "tcp" + cidr_block = "10.0.100.0/22" + }, + { + rule_number = 130 + rule_action = "allow" + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_block = "10.0.100.0/22" + }, + { + rule_number = 140 + rule_action = "allow" + icmp_code = -1 + icmp_type = 8 + protocol = "icmp" + cidr_block = "10.0.0.0/22" + }, + { + rule_number = 150 + rule_action = "allow" + from_port = 90 + to_port = 90 + protocol = "tcp" + ipv6_cidr_block = "::/0" + }, + ] + } +} diff --git a/examples/outpost/outputs.tf b/examples/outpost/outputs.tf new file mode 100644 index 000000000..ff40ad182 --- /dev/null +++ b/examples/outpost/outputs.tf @@ -0,0 +1,39 @@ +# VPC +output "vpc_id" { + description = "The ID of the VPC" + value = module.vpc.vpc_id +} + +# CIDR blocks +output "vpc_cidr_block" { + description = "The CIDR block of the VPC" + value = module.vpc.vpc_cidr_block +} + +# Subnets +output "private_subnets" { + description = "List of IDs of private subnets" + value = module.vpc.private_subnets +} + +output "public_subnets" { + description = "List of IDs of public subnets" + value = module.vpc.public_subnets +} + +output "outpost_subnets" { + description = "List of IDs of private subnets" + value = module.vpc.outpost_subnets +} + +# NAT gateways +output "nat_public_ips" { + description = "List of public Elastic IPs created for AWS NAT Gateway" + value = module.vpc.nat_public_ips +} + +# AZs +output "azs" { + description = "A list of availability zones specified as argument to this module" + value = module.vpc.azs +} diff --git a/examples/outpost/variables.tf b/examples/outpost/variables.tf new file mode 100644 index 000000000..e69de29bb diff --git a/examples/outpost/versions.tf b/examples/outpost/versions.tf new file mode 100644 index 000000000..cf5a417d4 --- /dev/null +++ b/examples/outpost/versions.tf @@ -0,0 +1,7 @@ +terraform { + required_version = ">= 0.12.21" + + required_providers { + aws = ">= 3.5.0" + } +} diff --git a/main.tf b/main.tf index cf33ab61f..6388d4c14 100644 --- a/main.tf +++ b/main.tf @@ -419,6 +419,34 @@ resource "aws_subnet" "private" { ) } +################# +# Outpost subnet +################# +resource "aws_subnet" "outpost" { + count = var.create_vpc && length(var.outpost_subnets) > 0 ? length(var.outpost_subnets) : 0 + + vpc_id = local.vpc_id + cidr_block = var.outpost_subnets[count.index] + availability_zone = var.outpost_az + assign_ipv6_address_on_creation = var.outpost_subnet_assign_ipv6_address_on_creation == null ? var.assign_ipv6_address_on_creation : var.outpost_subnet_assign_ipv6_address_on_creation + + ipv6_cidr_block = var.enable_ipv6 && length(var.outpost_subnet_ipv6_prefixes) > 0 ? cidrsubnet(aws_vpc.this[0].ipv6_cidr_block, 8, var.outpost_subnet_ipv6_prefixes[count.index]) : null + + outpost_arn = var.outpost_arn + + tags = merge( + { + "Name" = format( + "%s-${var.outpost_subnet_suffix}-%s", + var.name, + var.outpost_az, + ) + }, + var.tags, + var.outpost_subnet_tags, + ) +} + ################## # Database subnet ################## @@ -585,6 +613,7 @@ resource "aws_default_network_acl" "this" { aws_subnet.database.*.id, aws_subnet.redshift.*.id, aws_subnet.elasticache.*.id, + aws_subnet.outpost.*.id, ])), compact(flatten([ aws_network_acl.public.*.subnet_ids, @@ -593,6 +622,7 @@ resource "aws_default_network_acl" "this" { aws_network_acl.database.*.subnet_ids, aws_network_acl.redshift.*.subnet_ids, aws_network_acl.elasticache.*.subnet_ids, + aws_network_acl.outpost.*.subnet_ids, ])) ) @@ -738,6 +768,58 @@ resource "aws_network_acl_rule" "private_outbound" { ipv6_cidr_block = lookup(var.private_outbound_acl_rules[count.index], "ipv6_cidr_block", null) } +####################### +# Outpost Network ACLs +####################### +resource "aws_network_acl" "outpost" { + count = var.create_vpc && var.outpost_dedicated_network_acl && length(var.outpost_subnets) > 0 ? 1 : 0 + + vpc_id = element(concat(aws_vpc.this.*.id, [""]), 0) + subnet_ids = aws_subnet.outpost.*.id + + tags = merge( + { + "Name" = format("%s-${var.outpost_subnet_suffix}", var.name) + }, + var.tags, + var.outpost_acl_tags, + ) +} + +resource "aws_network_acl_rule" "outpost_inbound" { + count = var.create_vpc && var.outpost_dedicated_network_acl && length(var.outpost_subnets) > 0 ? length(var.outpost_inbound_acl_rules) : 0 + + network_acl_id = aws_network_acl.outpost[0].id + + egress = false + rule_number = var.outpost_inbound_acl_rules[count.index]["rule_number"] + rule_action = var.outpost_inbound_acl_rules[count.index]["rule_action"] + from_port = lookup(var.outpost_inbound_acl_rules[count.index], "from_port", null) + to_port = lookup(var.outpost_inbound_acl_rules[count.index], "to_port", null) + icmp_code = lookup(var.outpost_inbound_acl_rules[count.index], "icmp_code", null) + icmp_type = lookup(var.outpost_inbound_acl_rules[count.index], "icmp_type", null) + protocol = var.outpost_inbound_acl_rules[count.index]["protocol"] + cidr_block = lookup(var.outpost_inbound_acl_rules[count.index], "cidr_block", null) + ipv6_cidr_block = lookup(var.outpost_inbound_acl_rules[count.index], "ipv6_cidr_block", null) +} + +resource "aws_network_acl_rule" "outpost_outbound" { + count = var.create_vpc && var.outpost_dedicated_network_acl && length(var.outpost_subnets) > 0 ? length(var.outpost_outbound_acl_rules) : 0 + + network_acl_id = aws_network_acl.outpost[0].id + + egress = true + rule_number = var.outpost_outbound_acl_rules[count.index]["rule_number"] + rule_action = var.outpost_outbound_acl_rules[count.index]["rule_action"] + from_port = lookup(var.outpost_outbound_acl_rules[count.index], "from_port", null) + to_port = lookup(var.outpost_outbound_acl_rules[count.index], "to_port", null) + icmp_code = lookup(var.outpost_outbound_acl_rules[count.index], "icmp_code", null) + icmp_type = lookup(var.outpost_outbound_acl_rules[count.index], "icmp_type", null) + protocol = var.outpost_outbound_acl_rules[count.index]["protocol"] + cidr_block = lookup(var.outpost_outbound_acl_rules[count.index], "cidr_block", null) + ipv6_cidr_block = lookup(var.outpost_outbound_acl_rules[count.index], "ipv6_cidr_block", null) +} + ######################## # Intra Network ACLs ######################## @@ -1042,6 +1124,16 @@ resource "aws_route_table_association" "private" { ) } +resource "aws_route_table_association" "outpost" { + count = var.create_vpc && length(var.outpost_subnets) > 0 ? length(var.outpost_subnets) : 0 + + subnet_id = element(aws_subnet.outpost.*.id, count.index) + route_table_id = element( + aws_route_table.private.*.id, + var.single_nat_gateway ? 0 : count.index, + ) +} + resource "aws_route_table_association" "database" { count = var.create_vpc && length(var.database_subnets) > 0 ? length(var.database_subnets) : 0 @@ -1201,3 +1293,4 @@ resource "aws_default_vpc" "this" { var.default_vpc_tags, ) } + diff --git a/outputs.tf b/outputs.tf index 16a9a6cad..a6e3a68dc 100644 --- a/outputs.tf +++ b/outputs.tf @@ -108,6 +108,26 @@ output "public_subnets_ipv6_cidr_blocks" { value = aws_subnet.public.*.ipv6_cidr_block } +output "outpost_subnets" { + description = "List of IDs of outpost subnets" + value = aws_subnet.outpost.*.id +} + +output "outpost_subnet_arns" { + description = "List of ARNs of outpost subnets" + value = aws_subnet.outpost.*.arn +} + +output "outpost_subnets_cidr_blocks" { + description = "List of cidr_blocks of outpost subnets" + value = aws_subnet.outpost.*.cidr_block +} + +output "outpost_subnets_ipv6_cidr_blocks" { + description = "List of IPv6 cidr_blocks of outpost subnets in an IPv6 enabled VPC" + value = aws_subnet.outpost.*.ipv6_cidr_block +} + output "database_subnets" { description = "List of IDs of database subnets" value = aws_subnet.database.*.id @@ -442,6 +462,16 @@ output "private_network_acl_arn" { value = concat(aws_network_acl.private.*.arn, [""])[0] } +output "outpost_network_acl_id" { + description = "ID of the outpost network ACL" + value = concat(aws_network_acl.outpost.*.id, [""])[0] +} + +output "outpost_network_acl_arn" { + description = "ARN of the outpost network ACL" + value = concat(aws_network_acl.outpost.*.arn, [""])[0] +} + output "intra_network_acl_id" { description = "ID of the intra network ACL" value = concat(aws_network_acl.intra.*.id, [""])[0] diff --git a/variables.tf b/variables.tf index e75560c40..f23e60790 100644 --- a/variables.tf +++ b/variables.tf @@ -34,6 +34,12 @@ variable "public_subnet_ipv6_prefixes" { default = [] } +variable "outpost_subnet_ipv6_prefixes" { + description = "Assigns IPv6 outpost subnet id based on the Amazon provided /56 prefix base 10 integer (0-256). Must be of equal length to the corresponding IPv4 subnet list" + type = list(string) + default = [] +} + variable "database_subnet_ipv6_prefixes" { description = "Assigns IPv6 database subnet id based on the Amazon provided /56 prefix base 10 integer (0-256). Must be of equal length to the corresponding IPv4 subnet list" type = list(string) @@ -76,6 +82,12 @@ variable "public_subnet_assign_ipv6_address_on_creation" { default = null } +variable "outpost_subnet_assign_ipv6_address_on_creation" { + description = "Assign IPv6 address on outpost subnet, must be disabled to change IPv6 CIDRs. This is the IPv6 equivalent of map_public_ip_on_launch" + type = bool + default = null +} + variable "database_subnet_assign_ipv6_address_on_creation" { description = "Assign IPv6 address on database subnet, must be disabled to change IPv6 CIDRs. This is the IPv6 equivalent of map_public_ip_on_launch" type = bool @@ -124,6 +136,12 @@ variable "private_subnet_suffix" { default = "private" } +variable "outpost_subnet_suffix" { + description = "Suffix to append to outpost subnets name" + type = string + default = "outpost" +} + variable "intra_subnet_suffix" { description = "Suffix to append to intra subnets name" type = string @@ -160,6 +178,12 @@ variable "private_subnets" { default = [] } +variable "outpost_subnets" { + description = "A list of outpost subnets inside the VPC" + type = list(string) + default = [] +} + variable "database_subnets" { description = "A list of database subnets" type = list(string) @@ -2267,6 +2291,12 @@ variable "private_subnet_tags" { default = {} } +variable "outpost_subnet_tags" { + description = "Additional tags for the outpost subnets" + type = map(string) + default = {} +} + variable "public_route_table_tags" { description = "Additional tags for the public route tables" type = map(string) @@ -2351,6 +2381,12 @@ variable "private_acl_tags" { default = {} } +variable "outpost_acl_tags" { + description = "Additional tags for the outpost subnets network ACL" + type = map(string) + default = {} +} + variable "intra_acl_tags" { description = "Additional tags for the intra subnets network ACL" type = map(string) @@ -2525,6 +2561,12 @@ variable "private_dedicated_network_acl" { default = false } +variable "outpost_dedicated_network_acl" { + description = "Whether to use dedicated network ACL (not default) and custom rules for outpost subnets" + type = bool + default = false +} + variable "intra_dedicated_network_acl" { description = "Whether to use dedicated network ACL (not default) and custom rules for intra subnets" type = bool @@ -2661,6 +2703,38 @@ variable "private_outbound_acl_rules" { ] } +variable "outpost_inbound_acl_rules" { + description = "Outpost subnets inbound network ACLs" + type = list(map(string)) + + default = [ + { + rule_number = 100 + rule_action = "allow" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_block = "0.0.0.0/0" + }, + ] +} + +variable "outpost_outbound_acl_rules" { + description = "Outpost subnets outbound network ACLs" + type = list(map(string)) + + default = [ + { + rule_number = 100 + rule_action = "allow" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_block = "0.0.0.0/0" + }, + ] +} + variable "intra_inbound_acl_rules" { description = "Intra subnets inbound network ACLs" type = list(map(string)) @@ -2902,3 +2976,15 @@ variable "create_egress_only_igw" { type = bool default = true } + +variable "outpost_arn" { + description = "ARN of Outpost you want to create a subnet in." + type = string + default = null +} + +variable "outpost_az" { + description = "AZ where Outpost is anchored." + type = string + default = null +}