From d19622ab718709132d917c2c5797dfc5b04a52be Mon Sep 17 00:00:00 2001 From: Alvin Jaison Date: Fri, 24 Oct 2025 23:26:26 +0100 Subject: [PATCH 1/3] feat: add EC2 Instance Connect Endpoint support --- .../ec2-instance-connect-endpoint/README.md | 22 ++++++++++ .../ec2-instance-connect-endpoint/main.tf | 42 +++++++++++++++++++ .../ec2-instance-connect-endpoint/outputs.tf | 4 ++ .../variables.tf | 5 +++ .../ec2-instance-connect-endpoint/versions.tf | 10 +++++ main.tf | 20 +++++++++ outputs.tf | 8 ++++ variables.tf | 28 +++++++++++++ 8 files changed, 139 insertions(+) create mode 100644 examples/ec2-instance-connect-endpoint/README.md create mode 100644 examples/ec2-instance-connect-endpoint/main.tf create mode 100644 examples/ec2-instance-connect-endpoint/outputs.tf create mode 100644 examples/ec2-instance-connect-endpoint/variables.tf create mode 100644 examples/ec2-instance-connect-endpoint/versions.tf diff --git a/examples/ec2-instance-connect-endpoint/README.md b/examples/ec2-instance-connect-endpoint/README.md new file mode 100644 index 000000000..6952a28f8 --- /dev/null +++ b/examples/ec2-instance-connect-endpoint/README.md @@ -0,0 +1,22 @@ +# Example: EC2 Instance Connect Endpoint + +This example demonstrates how to enable the EC2 Instance Connect Endpoint feature within the VPC module. + +## Usage + +```hcl +module "vpc" { + source = "../../" + + name = "example-vpc" + cidr = "10.0.0.0/16" + + azs = ["us-east-1a", "us-east-1b"] + private_subnets = ["10.0.1.0/24", "10.0.2.0/24"] + public_subnets = ["10.0.101.0/24", "10.0.102.0/24"] + + create_instance_connect_endpoint = true + instance_connect_subnet_id = element(module.vpc.private_subnets, 0) + instance_connect_security_group_ids = [aws_security_group.allow_ssh.id] + instance_connect_preserve_client_ip = false +} diff --git a/examples/ec2-instance-connect-endpoint/main.tf b/examples/ec2-instance-connect-endpoint/main.tf new file mode 100644 index 000000000..05aa9104a --- /dev/null +++ b/examples/ec2-instance-connect-endpoint/main.tf @@ -0,0 +1,42 @@ +provider "aws" { + region = "us-east-1" +} + +module "vpc" { + source = "../../" + + name = "example-vpc" + cidr = "10.0.0.0/16" + + azs = ["us-east-1a", "us-east-1b"] + private_subnets = ["10.0.1.0/24", "10.0.2.0/24"] + public_subnets = ["10.0.101.0/24", "10.0.102.0/24"] + + enable_nat_gateway = true + single_nat_gateway = true + + create_instance_connect_endpoint = true + instance_connect_subnet_id = element(module.vpc.private_subnets, 0) + instance_connect_security_group_ids = [aws_security_group.allow_ssh.id] + instance_connect_preserve_client_ip = false +} + +resource "aws_security_group" "allow_ssh" { + name = "allow-ssh" + description = "Allow SSH access for EC2 Instance Connect" + vpc_id = module.vpc.vpc_id + + ingress { + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } +} diff --git a/examples/ec2-instance-connect-endpoint/outputs.tf b/examples/ec2-instance-connect-endpoint/outputs.tf new file mode 100644 index 000000000..69e5cae7c --- /dev/null +++ b/examples/ec2-instance-connect-endpoint/outputs.tf @@ -0,0 +1,4 @@ +output "instance_connect_endpoint_id" { + description = "The ID of the EC2 Instance Connect Endpoint" + value = module.vpc.instance_connect_endpoint_id +} diff --git a/examples/ec2-instance-connect-endpoint/variables.tf b/examples/ec2-instance-connect-endpoint/variables.tf new file mode 100644 index 000000000..bdf45e005 --- /dev/null +++ b/examples/ec2-instance-connect-endpoint/variables.tf @@ -0,0 +1,5 @@ +variable "region" { + description = "AWS region for the example" + type = string + default = "us-east-1" +} diff --git a/examples/ec2-instance-connect-endpoint/versions.tf b/examples/ec2-instance-connect-endpoint/versions.tf new file mode 100644 index 000000000..02559d773 --- /dev/null +++ b/examples/ec2-instance-connect-endpoint/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.3.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.6.0" + } + } +} diff --git a/main.tf b/main.tf index 31deb5988..a4cdba475 100644 --- a/main.tf +++ b/main.tf @@ -1541,3 +1541,23 @@ resource "aws_default_route_table" "default" { var.default_route_table_tags, ) } + +################################################################################ +# Endpoints +################################################################################ + +resource "aws_ec2_instance_connect_endpoint" "this" { + count = var.create_instance_connect_endpoint ? 1 : 0 + + subnet_id = var.instance_connect_subnet_id + security_group_ids = var.instance_connect_security_group_ids + + preserve_client_ip = var.instance_connect_preserve_client_ip + + tags = merge( + var.tags, + { + "Name" = "${var.name}-instance-connect-endpoint" + } + ) +} diff --git a/outputs.tf b/outputs.tf index 1d1d2783a..8362faba5 100644 --- a/outputs.tf +++ b/outputs.tf @@ -667,3 +667,11 @@ output "name" { description = "The name of the VPC specified as argument to this module" value = var.name } + +################################################################################ +# EC2 Instance Connect Endpoint +################################################################################ +output "instance_connect_endpoint_id" { + description = "The ID of the EC2 Instance Connect Endpoint" + value = try(aws_ec2_instance_connect_endpoint.this[0].id, null) +} diff --git a/variables.tf b/variables.tf index ea23a3e52..daafaf083 100644 --- a/variables.tf +++ b/variables.tf @@ -1678,3 +1678,31 @@ variable "putin_khuylo" { type = bool default = true } + +################################################################################ +# Endpoints +################################################################################ + +variable "create_instance_connect_endpoint" { + description = "Whether to create an EC2 Instance Connect Endpoint" + type = bool + default = false +} + +variable "instance_connect_subnet_id" { + description = "The ID of the subnet in which to create the Instance Connect Endpoint" + type = string + default = null +} + +variable "instance_connect_security_group_ids" { + description = "List of security group IDs to associate with the Instance Connect Endpoint" + type = list(string) + default = [] +} + +variable "instance_connect_preserve_client_ip" { + description = "Whether to preserve the client IP address when connecting via EC2 Instance Connect Endpoint" + type = bool + default = false +} From 6d128a009ffa11fe3d5cdbcaba94b31d6246951c Mon Sep 17 00:00:00 2001 From: Alvin Jaison Date: Sat, 25 Oct 2025 00:05:34 +0100 Subject: [PATCH 2/3] feat: Add EC2 Instance Connect Endpoint support --- .../ec2-instance-connect-endpoint/main.tf | 32 +++++++++++++++---- .../variables.tf | 5 --- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/examples/ec2-instance-connect-endpoint/main.tf b/examples/ec2-instance-connect-endpoint/main.tf index 05aa9104a..bb060841f 100644 --- a/examples/ec2-instance-connect-endpoint/main.tf +++ b/examples/ec2-instance-connect-endpoint/main.tf @@ -1,24 +1,44 @@ provider "aws" { + region = local.region +} + +data "aws_availability_zones" "available" {} + +locals { + name = "ex-${basename(path.cwd)}" region = "us-east-1" + azs = slice(data.aws_availability_zones.available.names, 0, 2) + + tags = { + Example = local.name + GithubRepo = "terraform-aws-vpc" + GithubOrg = "terraform-aws-modules" + } } +################################################################################ +# EC2 Instance Connect Endpoint Example +################################################################################ + module "vpc" { source = "../../" - name = "example-vpc" + name = local.name cidr = "10.0.0.0/16" - azs = ["us-east-1a", "us-east-1b"] + azs = local.azs private_subnets = ["10.0.1.0/24", "10.0.2.0/24"] public_subnets = ["10.0.101.0/24", "10.0.102.0/24"] enable_nat_gateway = true single_nat_gateway = true - create_instance_connect_endpoint = true - instance_connect_subnet_id = element(module.vpc.private_subnets, 0) - instance_connect_security_group_ids = [aws_security_group.allow_ssh.id] - instance_connect_preserve_client_ip = false + create_instance_connect_endpoint = true + instance_connect_subnet_id = element(local.private_subnets, 0) + instance_connect_security_group_ids = [aws_security_group.allow_ssh.id] + instance_connect_preserve_client_ip = false + + tags = local.tags } resource "aws_security_group" "allow_ssh" { diff --git a/examples/ec2-instance-connect-endpoint/variables.tf b/examples/ec2-instance-connect-endpoint/variables.tf index bdf45e005..e69de29bb 100644 --- a/examples/ec2-instance-connect-endpoint/variables.tf +++ b/examples/ec2-instance-connect-endpoint/variables.tf @@ -1,5 +0,0 @@ -variable "region" { - description = "AWS region for the example" - type = string - default = "us-east-1" -} From 28df5f998c9a24b9375ee0b62e8c098b1843ffe4 Mon Sep 17 00:00:00 2001 From: Alvin Jaison Date: Sat, 25 Oct 2025 00:34:52 +0100 Subject: [PATCH 3/3] feat: Add EC2 Instance Connect Endpoint support --- README.md | 6 +++++ .../ec2-instance-connect-endpoint/main.tf | 27 ++++++++++++++----- wrappers/main.tf | 4 +++ 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 96e8267a0..ee221589e 100644 --- a/README.md +++ b/README.md @@ -264,6 +264,7 @@ No modules. | [aws_default_route_table.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/default_route_table) | resource | | [aws_default_security_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/default_security_group) | resource | | [aws_default_vpc.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/default_vpc) | resource | +| [aws_ec2_instance_connect_endpoint.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_instance_connect_endpoint) | resource | | [aws_egress_only_internet_gateway.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/egress_only_internet_gateway) | resource | | [aws_eip.nat](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eip) | resource | | [aws_elasticache_subnet_group.elasticache](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/elasticache_subnet_group) | resource | @@ -359,6 +360,7 @@ No modules. | [create\_flow\_log\_cloudwatch\_iam\_role](#input\_create\_flow\_log\_cloudwatch\_iam\_role) | Whether to create IAM role for VPC Flow Logs | `bool` | `false` | no | | [create\_flow\_log\_cloudwatch\_log\_group](#input\_create\_flow\_log\_cloudwatch\_log\_group) | Whether to create CloudWatch log group for VPC Flow Logs | `bool` | `false` | no | | [create\_igw](#input\_create\_igw) | Controls if an Internet Gateway is created for public subnets and the related routes that connect them | `bool` | `true` | no | +| [create\_instance\_connect\_endpoint](#input\_create\_instance\_connect\_endpoint) | Whether to create an EC2 Instance Connect Endpoint | `bool` | `false` | no | | [create\_multiple\_intra\_route\_tables](#input\_create\_multiple\_intra\_route\_tables) | Indicates whether to create a separate route table for each intra subnet. Default: `false` | `bool` | `false` | no | | [create\_multiple\_public\_route\_tables](#input\_create\_multiple\_public\_route\_tables) | Indicates whether to create a separate route table for each public subnet. Default: `false` | `bool` | `false` | no | | [create\_private\_nat\_gateway\_route](#input\_create\_private\_nat\_gateway\_route) | Controls if a nat gateway route should be created to give internet access to the private subnets | `bool` | `true` | no | @@ -456,6 +458,9 @@ No modules. | [flow\_log\_per\_hour\_partition](#input\_flow\_log\_per\_hour\_partition) | (Optional) Indicates whether to partition the flow log per hour. This reduces the cost and response time for queries | `bool` | `false` | no | | [flow\_log\_traffic\_type](#input\_flow\_log\_traffic\_type) | The type of traffic to capture. Valid values: ACCEPT, REJECT, ALL | `string` | `"ALL"` | no | | [igw\_tags](#input\_igw\_tags) | Additional tags for the internet gateway | `map(string)` | `{}` | no | +| [instance\_connect\_preserve\_client\_ip](#input\_instance\_connect\_preserve\_client\_ip) | Whether to preserve the client IP address when connecting via EC2 Instance Connect Endpoint | `bool` | `false` | no | +| [instance\_connect\_security\_group\_ids](#input\_instance\_connect\_security\_group\_ids) | List of security group IDs to associate with the Instance Connect Endpoint | `list(string)` | `[]` | no | +| [instance\_connect\_subnet\_id](#input\_instance\_connect\_subnet\_id) | The ID of the subnet in which to create the Instance Connect Endpoint | `string` | `null` | no | | [instance\_tenancy](#input\_instance\_tenancy) | A tenancy option for instances launched into the VPC | `string` | `"default"` | no | | [intra\_acl\_tags](#input\_intra\_acl\_tags) | Additional tags for the intra subnets network ACL | `map(string)` | `{}` | no | | [intra\_dedicated\_network\_acl](#input\_intra\_dedicated\_network\_acl) | Whether to use dedicated network ACL (not default) and custom rules for intra subnets | `bool` | `false` | no | @@ -632,6 +637,7 @@ No modules. | [elasticache\_subnets\_ipv6\_cidr\_blocks](#output\_elasticache\_subnets\_ipv6\_cidr\_blocks) | List of IPv6 cidr\_blocks of elasticache subnets in an IPv6 enabled VPC | | [igw\_arn](#output\_igw\_arn) | The ARN of the Internet Gateway | | [igw\_id](#output\_igw\_id) | The ID of the Internet Gateway | +| [instance\_connect\_endpoint\_id](#output\_instance\_connect\_endpoint\_id) | The ID of the EC2 Instance Connect Endpoint | | [intra\_network\_acl\_arn](#output\_intra\_network\_acl\_arn) | ARN of the intra network ACL | | [intra\_network\_acl\_id](#output\_intra\_network\_acl\_id) | ID of the intra network ACL | | [intra\_route\_table\_association\_ids](#output\_intra\_route\_table\_association\_ids) | List of IDs of the intra route table association | diff --git a/examples/ec2-instance-connect-endpoint/main.tf b/examples/ec2-instance-connect-endpoint/main.tf index bb060841f..365c05034 100644 --- a/examples/ec2-instance-connect-endpoint/main.tf +++ b/examples/ec2-instance-connect-endpoint/main.tf @@ -5,9 +5,11 @@ provider "aws" { data "aws_availability_zones" "available" {} locals { - name = "ex-${basename(path.cwd)}" - region = "us-east-1" - azs = slice(data.aws_availability_zones.available.names, 0, 2) + name = "ex-${basename(path.cwd)}" + region = "us-east-1" + azs = slice(data.aws_availability_zones.available.names, 0, 3) + private_subnets = ["10.0.1.0/24", "10.0.2.0/24"] + public_subnets = ["10.0.101.0/24", "10.0.102.0/24"] tags = { Example = local.name @@ -23,24 +25,31 @@ locals { module "vpc" { source = "../../" - name = local.name + name = "example-vpc" cidr = "10.0.0.0/16" azs = local.azs - private_subnets = ["10.0.1.0/24", "10.0.2.0/24"] - public_subnets = ["10.0.101.0/24", "10.0.102.0/24"] + private_subnets = local.private_subnets + public_subnets = local.public_subnets enable_nat_gateway = true single_nat_gateway = true + # EC2 Instance Connect Endpoint configuration create_instance_connect_endpoint = true instance_connect_subnet_id = element(local.private_subnets, 0) instance_connect_security_group_ids = [aws_security_group.allow_ssh.id] instance_connect_preserve_client_ip = false - tags = local.tags + tags = merge({ + Name = "example-vpc" + }, local.tags) } +################################################################################ +# Security Group for EC2 Instance Connect +################################################################################ + resource "aws_security_group" "allow_ssh" { name = "allow-ssh" description = "Allow SSH access for EC2 Instance Connect" @@ -59,4 +68,8 @@ resource "aws_security_group" "allow_ssh" { protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } + + tags = merge({ + Name = "allow-ssh" + }, local.tags) } diff --git a/wrappers/main.tf b/wrappers/main.tf index bef0c73fc..af1de062f 100644 --- a/wrappers/main.tf +++ b/wrappers/main.tf @@ -16,6 +16,7 @@ module "wrapper" { create_flow_log_cloudwatch_iam_role = try(each.value.create_flow_log_cloudwatch_iam_role, var.defaults.create_flow_log_cloudwatch_iam_role, false) create_flow_log_cloudwatch_log_group = try(each.value.create_flow_log_cloudwatch_log_group, var.defaults.create_flow_log_cloudwatch_log_group, false) create_igw = try(each.value.create_igw, var.defaults.create_igw, true) + create_instance_connect_endpoint = try(each.value.create_instance_connect_endpoint, var.defaults.create_instance_connect_endpoint, false) create_multiple_intra_route_tables = try(each.value.create_multiple_intra_route_tables, var.defaults.create_multiple_intra_route_tables, false) create_multiple_public_route_tables = try(each.value.create_multiple_public_route_tables, var.defaults.create_multiple_public_route_tables, false) create_private_nat_gateway_route = try(each.value.create_private_nat_gateway_route, var.defaults.create_private_nat_gateway_route, true) @@ -183,6 +184,9 @@ module "wrapper" { flow_log_per_hour_partition = try(each.value.flow_log_per_hour_partition, var.defaults.flow_log_per_hour_partition, false) flow_log_traffic_type = try(each.value.flow_log_traffic_type, var.defaults.flow_log_traffic_type, "ALL") igw_tags = try(each.value.igw_tags, var.defaults.igw_tags, {}) + instance_connect_preserve_client_ip = try(each.value.instance_connect_preserve_client_ip, var.defaults.instance_connect_preserve_client_ip, false) + instance_connect_security_group_ids = try(each.value.instance_connect_security_group_ids, var.defaults.instance_connect_security_group_ids, []) + instance_connect_subnet_id = try(each.value.instance_connect_subnet_id, var.defaults.instance_connect_subnet_id, null) instance_tenancy = try(each.value.instance_tenancy, var.defaults.instance_tenancy, "default") intra_acl_tags = try(each.value.intra_acl_tags, var.defaults.intra_acl_tags, {}) intra_dedicated_network_acl = try(each.value.intra_dedicated_network_acl, var.defaults.intra_dedicated_network_acl, false)