Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down Expand Up @@ -359,6 +360,7 @@ No modules.
| <a name="input_create_flow_log_cloudwatch_iam_role"></a> [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 |
| <a name="input_create_flow_log_cloudwatch_log_group"></a> [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 |
| <a name="input_create_igw"></a> [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 |
| <a name="input_create_instance_connect_endpoint"></a> [create\_instance\_connect\_endpoint](#input\_create\_instance\_connect\_endpoint) | Whether to create EC2 Instance Connect Endpoint(s) | `bool` | `false` | no |
| <a name="input_create_multiple_intra_route_tables"></a> [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 |
| <a name="input_create_multiple_public_route_tables"></a> [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 |
| <a name="input_create_private_nat_gateway_route"></a> [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 |
Expand Down Expand Up @@ -456,6 +458,11 @@ No modules.
| <a name="input_flow_log_per_hour_partition"></a> [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 |
| <a name="input_flow_log_traffic_type"></a> [flow\_log\_traffic\_type](#input\_flow\_log\_traffic\_type) | The type of traffic to capture. Valid values: ACCEPT, REJECT, ALL | `string` | `"ALL"` | no |
| <a name="input_igw_tags"></a> [igw\_tags](#input\_igw\_tags) | Additional tags for the internet gateway | `map(string)` | `{}` | no |
| <a name="input_instance_connect_endpoint_create_in_private_subnets"></a> [instance\_connect\_endpoint\_create\_in\_private\_subnets](#input\_instance\_connect\_endpoint\_create\_in\_private\_subnets) | Create EC2 Instance Connect Endpoint(s) in all private subnets if no subnet IDs are provided | `bool` | `true` | no |
| <a name="input_instance_connect_endpoint_subnets"></a> [instance\_connect\_endpoint\_subnets](#input\_instance\_connect\_endpoint\_subnets) | List of subnet IDs where EC2 Instance Connect Endpoint(s) should be created. If null and create\_in\_private\_subnets is true, defaults to private subnets | `list(string)` | `null` | no |
| <a name="input_instance_connect_preserve_client_ip"></a> [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 |
| <a name="input_instance_connect_security_group_ids"></a> [instance\_connect\_security\_group\_ids](#input\_instance\_connect\_security\_group\_ids) | List of security group IDs to associate with EC2 Instance Connect Endpoint(s). If null, defaults to no security groups | `list(string)` | `null` | no |
| <a name="input_instance_connect_tags"></a> [instance\_connect\_tags](#input\_instance\_connect\_tags) | Additional tags for EC2 Instance Connect Endpoint resources | `map(string)` | `{}` | no |
| <a name="input_instance_tenancy"></a> [instance\_tenancy](#input\_instance\_tenancy) | A tenancy option for instances launched into the VPC | `string` | `"default"` | no |
| <a name="input_intra_acl_tags"></a> [intra\_acl\_tags](#input\_intra\_acl\_tags) | Additional tags for the intra subnets network ACL | `map(string)` | `{}` | no |
| <a name="input_intra_dedicated_network_acl"></a> [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 |
Expand Down Expand Up @@ -632,6 +639,7 @@ No modules.
| <a name="output_elasticache_subnets_ipv6_cidr_blocks"></a> [elasticache\_subnets\_ipv6\_cidr\_blocks](#output\_elasticache\_subnets\_ipv6\_cidr\_blocks) | List of IPv6 cidr\_blocks of elasticache subnets in an IPv6 enabled VPC |
| <a name="output_igw_arn"></a> [igw\_arn](#output\_igw\_arn) | The ARN of the Internet Gateway |
| <a name="output_igw_id"></a> [igw\_id](#output\_igw\_id) | The ID of the Internet Gateway |
| <a name="output_instance_connect_endpoints"></a> [instance\_connect\_endpoints](#output\_instance\_connect\_endpoints) | Map of EC2 Instance Connect Endpoints created, keyed by subnet index |
| <a name="output_intra_network_acl_arn"></a> [intra\_network\_acl\_arn](#output\_intra\_network\_acl\_arn) | ARN of the intra network ACL |
| <a name="output_intra_network_acl_id"></a> [intra\_network\_acl\_id](#output\_intra\_network\_acl\_id) | ID of the intra network ACL |
| <a name="output_intra_route_table_association_ids"></a> [intra\_route\_table\_association\_ids](#output\_intra\_route\_table\_association\_ids) | List of IDs of the intra route table association |
Expand Down
84 changes: 84 additions & 0 deletions examples/ec2-instance-connect-endpoint/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# EC2 Instance Connect Endpoint

Configuration in this directory creates a **VPC** with **EC2 Instance Connect Endpoints (EICE)** deployed in private subnets.
This allows secure SSH or RDP access to instances **without using a bastion host** or assigning public IPs.

## Features

This example demonstrates:

- Deployment of a VPC with public and private subnets across multiple Availability Zones
- Creation of a **NAT Gateway** for outbound internet access
- Deployment of **EC2 Instance Connect Endpoints** in private subnets
- Creation of a security group allowing SSH access
- Demonstration of the `preserve_client_ip` and tagging features for EICE resources

---

## Usage

To run this example, execute:

```bash
terraform init
terraform plan
terraform apply
```

⚠️ Note:
This example may create resources that incur costs (such as VPCs, NAT Gateways, and EC2 endpoints).
Run terraform destroy when you no longer need these resources.

<!-- BEGIN_TF_DOCS -->
## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.5.7 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 6.5 |

## Providers

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

## Modules

| Name | Source | Version |
|------|--------|---------|
| <a name="module_vpc"></a> [vpc](#module\_vpc) | ../.. | n/a |

## Resources

| Name | Type |
|------|------|
| [aws_security_group.allow_ssh](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source |

## Inputs

No inputs.

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_instance_connect_endpoints"></a> [instance\_connect\_endpoints](#output\_instance\_connect\_endpoints) | Map of EC2 Instance Connect Endpoints created |
| <a name="output_vpc_id"></a> [vpc\_id](#output\_vpc\_id) | The ID of the VPC |
<!-- END_TF_DOCS -->

##Example Output

Example output after deployment:

```bash
instance_connect_endpoints = {
"0" = {
"id" = "eice-0c123456789abcd"
"arn" = "arn:aws:ec2:us-east-1:123456789012:ec2-instance-connect-endpoint/eice-0c123456789abcd"
"subnet_id" = "subnet-0123456789abcdef"
"sg_ids" = ["sg-0123456789abcdef"]
}
}
```
92 changes: 92 additions & 0 deletions examples/ec2-instance-connect-endpoint/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
################################################################################
# Provider Configuration
################################################################################

provider "aws" {
region = local.region
}

data "aws_availability_zones" "available" {}

################################################################################
# Locals
################################################################################

locals {
name = "ex-${basename(path.cwd)}"
region = "us-east-1"

# Get the first 2 availability zones for simplicity
azs = slice(data.aws_availability_zones.available.names, 0, 2)

# Base VPC CIDR
vpc_cidr = "10.0.0.0/16"

# Calculate subnet CIDRs automatically based on AZ count
private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)]
public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k + 10)]

tags = {
Example = local.name
GithubRepo = "terraform-aws-vpc"
GithubOrg = "terraform-aws-modules"
}
}

################################################################################
# VPC Module
################################################################################

module "vpc" {
source = "../.."

name = local.name
cidr = local.vpc_cidr

azs = local.azs
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_preserve_client_ip = true
instance_connect_security_group_ids = [aws_security_group.allow_ssh.id]
instance_connect_tags = {
Environment = "example"
}

tags = merge(local.tags, {
Name = local.name
})
}

################################################################################
# Security Group for EC2 Instance Connect
################################################################################

resource "aws_security_group" "allow_ssh" {
name = "${local.name}-allow-ssh"
description = "Allow SSH 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"]
}

tags = merge(local.tags, {
Name = "${local.name}-allow-ssh"
})
}
9 changes: 9 additions & 0 deletions examples/ec2-instance-connect-endpoint/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
output "vpc_id" {
description = "The ID of the VPC"
value = module.vpc.vpc_id
}

output "instance_connect_endpoints" {
description = "Map of EC2 Instance Connect Endpoints created"
value = module.vpc.instance_connect_endpoints
}
Empty file.
10 changes: 10 additions & 0 deletions examples/ec2-instance-connect-endpoint/versions.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
terraform {
required_version = ">= 1.5.7"

required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 6.5"
}
}
}
40 changes: 40 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,20 @@ locals {
vpc_id = try(aws_vpc_ipv4_cidr_block_association.this[0].vpc_id, aws_vpc.this[0].id, "")

create_vpc = var.create_vpc && var.putin_khuylo

# EC2 Instance Connect Endpoint target subnets
instance_connect_target_subnets = (
var.instance_connect_endpoint_subnets != null && length(var.instance_connect_endpoint_subnets) > 0
? var.instance_connect_endpoint_subnets
: (
var.instance_connect_endpoint_create_in_private_subnets && local.len_private_subnets > 0
? aws_subnet.private[*].id
: []
)
)
}


################################################################################
# VPC
################################################################################
Expand Down Expand Up @@ -1541,3 +1553,31 @@ resource "aws_default_route_table" "default" {
var.default_route_table_tags,
)
}

################################################################################
# EC2 Instance Connect Endpoint
################################################################################

resource "aws_ec2_instance_connect_endpoint" "this" {
for_each = var.create_instance_connect_endpoint ? {
for idx, subnet_id in local.instance_connect_target_subnets : idx => subnet_id
} : {}

subnet_id = each.value

security_group_ids = (
var.instance_connect_security_group_ids != null
? var.instance_connect_security_group_ids
: []
)

preserve_client_ip = var.instance_connect_preserve_client_ip

tags = merge(
var.tags,
var.instance_connect_tags,
{
Name = "${var.name}-ec2-instance-connect-${each.key}"
}
)
}
16 changes: 16 additions & 0 deletions outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -667,3 +667,19 @@ output "name" {
description = "The name of the VPC specified as argument to this module"
value = var.name
}

################################################################################
# EC2 Instance Connect Endpoint
################################################################################

output "instance_connect_endpoints" {
description = "Map of EC2 Instance Connect Endpoints created, keyed by subnet index"
value = try({
for k, v in aws_ec2_instance_connect_endpoint.this : k => {
id = v.id
arn = v.arn
subnet_id = v.subnet_id
sg_ids = v.security_group_ids
}
}, {})
}
40 changes: 40 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -1678,3 +1678,43 @@ variable "putin_khuylo" {
type = bool
default = true
}

################################################################################
# EC2 Instance Connect Endpoint
################################################################################

variable "create_instance_connect_endpoint" {
type = bool
default = false
description = "Whether to create EC2 Instance Connect Endpoint(s)"
}

variable "instance_connect_endpoint_create_in_private_subnets" {
type = bool
default = true
description = "Create EC2 Instance Connect Endpoint(s) in all private subnets if no subnet IDs are provided"
}

variable "instance_connect_endpoint_subnets" {
type = list(string)
default = null
description = "List of subnet IDs where EC2 Instance Connect Endpoint(s) should be created. If null and create_in_private_subnets is true, defaults to private subnets"
}

variable "instance_connect_security_group_ids" {
type = list(string)
default = null
description = "List of security group IDs to associate with EC2 Instance Connect Endpoint(s). If null, defaults to no security groups"
}

variable "instance_connect_tags" {
type = map(string)
default = {}
description = "Additional tags for EC2 Instance Connect Endpoint resources"
}

variable "instance_connect_preserve_client_ip" {
type = bool
default = false
description = "Whether to preserve the client IP address when connecting via EC2 Instance Connect Endpoint"
}
6 changes: 6 additions & 0 deletions wrappers/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -183,6 +184,11 @@ 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_endpoint_create_in_private_subnets = try(each.value.instance_connect_endpoint_create_in_private_subnets, var.defaults.instance_connect_endpoint_create_in_private_subnets, true)
instance_connect_endpoint_subnets = try(each.value.instance_connect_endpoint_subnets, var.defaults.instance_connect_endpoint_subnets, null)
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, null)
instance_connect_tags = try(each.value.instance_connect_tags, var.defaults.instance_connect_tags, {})
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)
Expand Down
Loading