feat(devops): Add AWS terraform (#3298)

Why:

* Previously the terraform for all of the AWS infra was created and run
outside of the mono repo. While this was very quick to setup and work
with, keeping the gateway up to date was easy to forget about. Moving
all of the AWS infra TF into the mono repo will allow everything to stay
up to date and will make sure everyone has easy access to update any of
the infra as needed.

---------

Co-authored-by: Jamil <jamilbk@users.noreply.github.com>
This commit is contained in:
Brian Manifold
2024-01-18 15:38:55 -05:00
committed by GitHub
parent 3fcaf684cc
commit 2a62e3961e
22 changed files with 1215 additions and 10 deletions

View File

@@ -3,7 +3,8 @@ provider "aws" {
}
locals {
aws_region = "us-east-1"
aws_region = "us-east-1"
environment = "staging"
vpc_name = "Staging"
vpc_cidr = "10.0.0.0/16"
@@ -14,7 +15,7 @@ locals {
tags = {
Terraform = true
Environment = "staging"
Environment = local.environment
}
}
@@ -28,16 +29,204 @@ module "vpc" {
name = local.vpc_name
cidr = local.vpc_cidr
enable_ipv6 = true
public_subnet_assign_ipv6_address_on_creation = true
azs = local.azs
public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)]
private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k + local.num_azs)]
private_subnet_enable_dns64 = false
private_subnet_enable_resource_name_dns_aaaa_record_on_launch = false
enable_ipv6 = true
public_subnet_assign_ipv6_address_on_creation = true
private_subnet_assign_ipv6_address_on_creation = true
azs = local.azs
public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)]
private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k + local.num_azs)]
public_subnet_ipv6_prefixes = range(0, local.num_azs)
public_subnet_ipv6_prefixes = [0, 1]
private_subnet_ipv6_prefixes = [2, 3]
tags = local.tags
}
resource "aws_route" "private_nat_instance" {
count = local.num_azs
route_table_id = element(module.vpc.private_route_table_ids, count.index)
destination_cidr_block = "0.0.0.0/0"
network_interface_id = module.aws_nat.primary_network_interface_id
timeouts {
create = "5m"
}
}
################################################################################
# Compute
################################################################################
module "aws_bastion" {
source = "../../modules/aws/bastion"
ami = data.aws_ami.ubuntu.id
name = "bastion - ${local.environment}"
associate_public_ip_address = true
instance_type = "t3.micro"
key_name = local.ssh_keypair_name
vpc_security_group_ids = [
module.sg_allow_all_egress.security_group_id,
module.sg_allow_ssh_ingress.security_group_id
]
subnet_id = element(module.vpc.public_subnets, 0)
tags = local.tags
}
module "aws_nat" {
source = "../../modules/aws/nat"
ami = data.aws_ami.ubuntu.id
name = "nat - ${local.environment}"
associate_public_ip_address = true
instance_type = "t3.micro"
key_name = local.ssh_keypair_name
subnet_id = element(module.vpc.public_subnets, 0)
vpc_security_group_ids = [
module.sg_allow_all_egress.security_group_id,
module.sg_allow_subnet_ingress.security_group_id
]
tags = local.tags
}
module "aws_httpbin" {
source = "../../modules/aws/httpbin"
ami = data.aws_ami.ubuntu.id
name = "httpbin - ${local.environment}"
associate_public_ip_address = false
instance_type = "t3.micro"
key_name = local.ssh_keypair_name
subnet_id = element(module.vpc.private_subnets, 0)
private_ip = cidrhost(element(module.vpc.private_subnets_cidr_blocks, 0), 100)
vpc_security_group_ids = [
module.sg_allow_all_egress.security_group_id,
module.sg_allow_subnet_ingress.security_group_id
]
tags = local.tags
}
module "aws_iperf" {
source = "../../modules/aws/iperf"
ami = data.aws_ami.ubuntu.id
name = "iperf - ${local.environment}"
associate_public_ip_address = false
instance_type = "t3.micro"
key_name = local.ssh_keypair_name
subnet_id = element(module.vpc.private_subnets, 0)
private_ip = cidrhost(element(module.vpc.private_subnets_cidr_blocks, 0), 101)
vpc_security_group_ids = [
module.sg_allow_all_egress.security_group_id,
module.sg_allow_subnet_ingress.security_group_id
]
tags = local.tags
}
module "aws_gateway" {
source = "../../modules/aws/gateway"
ami = data.aws_ami.ubuntu.id
name = "gateway - ${local.environment}"
associate_public_ip_address = false
instance_type = "t3.micro"
key_name = local.ssh_keypair_name
subnet_id = element(module.vpc.private_subnets, 0)
private_ip = cidrhost(element(module.vpc.private_subnets_cidr_blocks, 0), 50)
vpc_security_group_ids = [
module.sg_allow_all_egress.security_group_id,
module.sg_allow_subnet_ingress.security_group_id
]
# Gateway specific vars
container_registry = module.google-artifact-registry.url
image_repo = module.google-artifact-registry.repo
image = "gateway"
image_tag = var.image_tag
observability_log_level = "firezone_gateway=trace,wire=trace,connlib_gateway_shared=trace,firezone_tunnel=trace,connlib_shared=trace,warn"
application_name = "gateway"
application_version = replace(var.image_tag, ".", "-")
api_url = "wss://api.${local.tld}"
token = var.aws_gateway_token
tags = local.tags
}
################################################################################
# Security Groups
################################################################################
module "sg_allow_all_egress" {
source = "terraform-aws-modules/security-group/aws"
name = "allow all egress"
description = "Security group to allow all egress"
vpc_id = module.vpc.vpc_id
egress_with_cidr_blocks = [
{
rule = "all-all"
cidr_blocks = "0.0.0.0/0"
},
]
egress_with_ipv6_cidr_blocks = [
{
rule = "all-all"
ipv6_cidr_blocks = "::/0"
},
]
}
module "sg_allow_subnet_ingress" {
source = "terraform-aws-modules/security-group/aws"
name = "allow ingress from subnet"
description = "Security group to allow all ingress from other machines on the subnet"
vpc_id = module.vpc.vpc_id
ingress_with_cidr_blocks = [
{
rule = "all-all"
cidr_blocks = join(",", module.vpc.public_subnets_cidr_blocks)
},
{
rule = "all-all",
cidr_blocks = join(",", module.vpc.private_subnets_cidr_blocks)
}
]
}
module "sg_allow_ssh_ingress" {
source = "terraform-aws-modules/security-group/aws"
name = "allow SSH ingress from the internet"
description = "Security group to allow SSH ingress from the internet"
vpc_id = module.vpc.vpc_id
ingress_with_cidr_blocks = [
{
from_port = 22
to_port = 22
protocol = "tcp"
description = "SSH access from the internet"
cidr_blocks = "0.0.0.0/0"
}
]
}

View File

@@ -1,3 +1,9 @@
variable "aws_gateway_token" {
type = string
description = "Firezone Gateway token for AWS gateway"
default = null
}
variable "image_tag" {
type = string
description = "Image tag for all services. Notice: we assume all services are deployed with the same version"

View File

@@ -0,0 +1,18 @@
resource "aws_instance" "this" {
ami = var.ami
instance_type = var.instance_type
monitoring = var.monitoring
subnet_id = var.subnet_id
vpc_security_group_ids = var.vpc_security_group_ids
associate_public_ip_address = var.associate_public_ip_address
key_name = var.key_name
user_data = file("${path.module}/scripts/setup.sh")
root_block_device {
volume_type = "gp3"
volume_size = 20
}
tags = merge({ "Name" = var.name }, var.instance_tags, var.tags)
}

View File

@@ -0,0 +1,55 @@
output "id" {
description = "The ID of the instance"
value = try(
aws_instance.this.id,
null,
)
}
output "arn" {
description = "The ARN of the instance"
value = try(
aws_instance.this.arn,
null,
)
}
output "instance_state" {
description = "The state of the instance"
value = try(
aws_instance.this.instance_state,
null,
)
}
output "primary_network_interface_id" {
description = "The ID of the instance's primary network interface"
value = try(
aws_instance.this.primary_network_interface_id,
null,
)
}
output "public_ip" {
description = "The public IP address assigned to the instance, if applicable. NOTE: If you are using an aws_eip with your instance, you should refer to the EIP's address directly and not use `public_ip` as this field will change after the EIP is attached"
value = try(
aws_instance.this.public_ip,
null,
)
}
output "private_ip" {
description = "The private IP address assigned to the instance"
value = try(
aws_instance.this.private_ip,
null,
)
}
output "ipv6_addresses" {
description = "The IPv6 address assigned to the instance, if applicable"
value = try(
aws_instance.this.ipv6_addresses,
[],
)
}

View File

@@ -0,0 +1,34 @@
#!/bin/bash
set -xe
# Install fail2ban
sudo apt-get update
sudo apt-get install -y fail2ban
ORIG_CONF="/etc/fail2ban/jail.conf"
LOCAL_CONF="/etc/fail2ban/jail.local"
if [ -f "${ORIG_CONF}" ]; then
# Configure fail2ban
sudo cp "${ORIG_CONF}" "${LOCAL_CONF}"
sudo sed -i 's/^bantime\s*= 10m$/bantime = 30m/' "${LOCAL_CONF}"
sudo sed -i 's/^findtime\s*= 10m/findtime = 30m/' "${LOCAL_CONF}"
sudo sed -i 's/maxretry\s*= 5/maxretry = 3/' "${LOCAL_CONF}"
# Enable and Start fail2ban
sudo systemctl enable --now fail2ban
else
# If fail2ban is not on the sysytem, something has gone wrong
echo "Fail2Ban was not found on the system! Exiting..."
fi
# Turn on automatic upgrades/reboots
UPGRADE_CONF_FILE="/etc/apt/apt.conf.d/50unattended-upgrades"
sudo cp $UPGRADE_CONF_FILE /tmp/unattended-upgrades.conf
sudo sed -i 's/\/\/\(\s*"\${distro_id}:\${distro_codename}-updates";\)/ \1/' "${UPGRADE_CONF_FILE}"
sudo sed -i 's/\/\/\(Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";\)/\1/' "${UPGRADE_CONF_FILE}"
sudo sed -i 's/\/\/\(Unattended-Upgrade::Automatic-Reboot \)"false";/\1 "true";/' "${UPGRADE_CONF_FILE}"
sudo sed -i 's/\/\/\(Unattended-Upgrade::Automatic-Reboot-Time \)"02:00";/\1 "07:00;"/' "${UPGRADE_CONF_FILE}"
sudo sed -i 's/\/\/\(Unattended-Upgrade::Automatic-Reboot-WithUsers "true";\)/\1/' "${UPGRADE_CONF_FILE}"

View File

@@ -0,0 +1,82 @@
variable "ami" {
type = string
description = "AMI ID for the EC2 instance"
default = "ami-0b2a9065573b0a9c9" # Ubuntu 22.04 in us-east-1
validation {
condition = length(var.ami) > 4 && substr(var.ami, 0, 4) == "ami-"
error_message = "Please provide a valid value for variable AMI."
}
}
variable "associate_public_ip_address" {
description = "Whether to associate a public IP address with an instance in a VPC"
type = bool
default = true
}
variable "instance_type" {
description = "The type of instance to start"
type = string
default = "t3.micro"
}
variable "instance_tags" {
description = "Additional tags for the instance"
type = map(string)
default = {}
}
variable "ipv6_addresses" {
description = "Specify one or more IPv6 addresses from the range of the subnet to associate with the primary network interface"
type = list(string)
default = null
}
variable "key_name" {
description = "Key name of the Key Pair to use for the instance; which can be managed using the `aws_key_pair` resource"
type = string
default = null
}
variable "monitoring" {
description = "If true, the launched EC2 instance will have detailed monitoring enabled"
type = bool
default = false
}
variable "name" {
description = "Name to be used on EC2 instance created"
type = string
default = ""
}
variable "private_ip" {
description = "Private IP address to associate with the instance in a VPC"
type = string
default = null
}
variable "root_block_device" {
description = "Customize details about the root block device of the instance. See Block Devices below for details"
type = list(any)
default = []
}
variable "subnet_id" {
description = "The VPC Subnet ID to launch in"
type = string
default = null
}
variable "tags" {
description = "A mapping of tags to assign to the resource"
type = map(string)
default = {}
}
variable "vpc_security_group_ids" {
description = "A list of security group IDs to associate with"
type = list(string)
default = null
}

View File

@@ -0,0 +1,51 @@
locals {
application_name = var.application_name != null ? var.application_name : var.image
application_version = var.application_version != null ? var.application_version : var.image_tag
environment_variables = concat([
{
name = "RUST_LOG"
value = var.observability_log_level
},
{
name = "RUST_BACKTRACE"
value = "full"
},
{
name = "FIREZONE_TOKEN"
value = var.token
},
{
name = "FIREZONE_API_URL"
value = var.api_url
},
{
name = "FIREZONE_ENABLE_MASQUERADE"
value = "1"
}
], var.application_environment_variables)
}
resource "aws_instance" "this" {
ami = var.ami
instance_type = var.instance_type
monitoring = var.monitoring
subnet_id = var.subnet_id
vpc_security_group_ids = var.vpc_security_group_ids
associate_public_ip_address = var.associate_public_ip_address
private_ip = var.private_ip
key_name = var.key_name
user_data = templatefile("${path.module}/templates/cloud-init.yaml", {
container_name = local.application_name != null ? local.application_name : var.image
container_image = "${var.container_registry}/${var.image_repo}/${var.image}:${var.image_tag}"
container_environment = local.environment_variables
})
root_block_device {
volume_type = "gp3"
volume_size = 20
}
tags = merge({ "Name" = var.name }, var.instance_tags, var.tags)
}

View File

@@ -0,0 +1,55 @@
output "id" {
description = "The ID of the instance"
value = try(
aws_instance.this.id,
null,
)
}
output "arn" {
description = "The ARN of the instance"
value = try(
aws_instance.this.arn,
null,
)
}
output "instance_state" {
description = "The state of the instance"
value = try(
aws_instance.this.instance_state,
null,
)
}
output "primary_network_interface_id" {
description = "The ID of the instance's primary network interface"
value = try(
aws_instance.this.primary_network_interface_id,
null,
)
}
output "public_ip" {
description = "The public IP address assigned to the instance, if applicable. NOTE: If you are using an aws_eip with your instance, you should refer to the EIP's address directly and not use `public_ip` as this field will change after the EIP is attached"
value = try(
aws_instance.this.public_ip,
null,
)
}
output "private_ip" {
description = "The private IP address assigned to the instance"
value = try(
aws_instance.this.private_ip,
null,
)
}
output "ipv6_addresses" {
description = "The IPv6 address assigned to the instance, if applicable"
value = try(
aws_instance.this.ipv6_addresses,
[],
)
}

View File

@@ -0,0 +1,40 @@
#cloud-config
write_files:
- path: /etc/firezone-gateway/env
permissions: "0644"
owner: root
content: |
%{ for env in container_environment ~}
${env.name}=${env.value}
%{ endfor ~}
- path: /etc/systemd/system/gateway.service
permissions: "0644"
owner: root
content: |
[Unit]
Description=Start an Firezone Gateway container
[Service]
TimeoutStartSec=0
Restart=always
ExecStartPre=/usr/bin/docker pull ${container_image}
ExecStart=/bin/sh -c 'docker run --rm --name=${container_name} --cap-add=NET_ADMIN --volume /etc/firezone --device="/dev/net/tun:/dev/net/tun" --env FIREZONE_NAME=$(hostname) --env FIREZONE_ID=$(echo $RANDOM$(hostname) | md5sum | head -c 20; echo;) --env-file="/etc/firezone-gateway/env" ${container_image}'
ExecStop=/usr/bin/docker stop gateway
ExecStopPost=/usr/bin/docker rm gateway
runcmd:
- sudo apt-get update
- sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common
- curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
- echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
- sudo apt-get update
- sudo apt-get install -y docker-ce docker-ce-cli containerd.io
- echo '{"experimental":true,"ip6tables":true,"ipv6":true,"fixed-cidr-v6":"fd00::/80"}' | sudo tee -a /etc/docker/daemon.json
- sudo usermod -aG docker ubuntu
- sudo systemctl enable docker
- sudo systemctl stop docker
- sudo systemctl start docker
- sudo systemctl daemon-reload
- sudo systemctl start gateway.service

View File

@@ -0,0 +1,150 @@
variable "ami" {
description = "AMI ID for the EC2 instance"
type = string
default = "ami-0b2a9065573b0a9c9" # Ubuntu 22.04 in us-east-1
validation {
condition = length(var.ami) > 4 && substr(var.ami, 0, 4) == "ami-"
error_message = "Please provide a valid value for variable AMI."
}
}
variable "api_url" {
description = "URL of the control plane endpoint."
type = string
default = null
}
variable "application_environment_variables" {
description = "List of environment variables to set for all application containers."
type = list(object({
name = string
value = string
}))
default = []
nullable = false
}
variable "application_name" {
description = "Name of the application. Defaults to value of `var.image_name` with `_` replaced to `-`."
type = string
nullable = true
default = null
}
variable "application_version" {
description = "Version of the application. Defaults to value of `var.image_tag`."
type = string
nullable = true
default = null
}
variable "associate_public_ip_address" {
description = "Whether to associate a public IP address with an instance in a VPC"
type = bool
default = true
}
variable "container_registry" {
description = "Container registry URL to pull the image from."
type = string
nullable = false
}
variable "image" {
description = "Container image used to deploy the application."
type = string
nullable = false
}
variable "image_repo" {
description = "Repo of a container image used to deploy the application."
type = string
nullable = false
}
variable "image_tag" {
description = "Container image used to deploy the application."
type = string
nullable = false
}
variable "instance_type" {
description = "The type of instance to start"
type = string
default = "t3.micro"
}
variable "instance_tags" {
description = "Additional tags for the instance"
type = map(string)
default = {}
}
variable "ipv6_addresses" {
description = "Specify one or more IPv6 addresses from the range of the subnet to associate with the primary network interface"
type = list(string)
default = null
}
variable "key_name" {
description = "Key name of the Key Pair to use for the instance; which can be managed using the `aws_key_pair` resource"
type = string
default = null
}
variable "monitoring" {
description = "If true, the launched EC2 instance will have detailed monitoring enabled"
type = bool
default = null
}
variable "name" {
description = "Name to be used on EC2 instance created"
type = string
default = ""
}
variable "observability_log_level" {
description = "Sets RUST_LOG environment variable which applications should use to configure Rust Logger. Default: 'info'."
type = string
nullable = false
default = "info"
}
variable "private_ip" {
description = "Private IP address to associate with the instance in a VPC"
type = string
default = null
}
variable "root_block_device" {
description = "Customize details about the root block device of the instance. See Block Devices below for details"
type = list(any)
default = []
}
variable "subnet_id" {
description = "The VPC Subnet ID to launch in"
type = string
default = null
}
variable "tags" {
description = "A mapping of tags to assign to the resource"
type = map(string)
default = {}
}
variable "token" {
description = "Portal token to use for authentication."
type = string
default = null
}
variable "vpc_security_group_ids" {
description = "A list of security group IDs to associate with"
type = list(string)
default = null
}

View File

@@ -0,0 +1,19 @@
resource "aws_instance" "this" {
ami = var.ami
instance_type = var.instance_type
monitoring = var.monitoring
subnet_id = var.subnet_id
vpc_security_group_ids = var.vpc_security_group_ids
associate_public_ip_address = var.associate_public_ip_address
private_ip = var.private_ip
key_name = var.key_name
user_data = file("${path.module}/scripts/setup.sh")
root_block_device {
volume_type = "gp3"
volume_size = 20
}
tags = merge({ "Name" = var.name }, var.instance_tags, var.tags)
}

View File

@@ -0,0 +1,55 @@
output "id" {
description = "The ID of the instance"
value = try(
aws_instance.this.id,
null,
)
}
output "arn" {
description = "The ARN of the instance"
value = try(
aws_instance.this.arn,
null,
)
}
output "instance_state" {
description = "The state of the instance"
value = try(
aws_instance.this.instance_state,
null,
)
}
output "primary_network_interface_id" {
description = "The ID of the instance's primary network interface"
value = try(
aws_instance.this.primary_network_interface_id,
null,
)
}
output "public_ip" {
description = "The public IP address assigned to the instance, if applicable. NOTE: If you are using an aws_eip with your instance, you should refer to the EIP's address directly and not use `public_ip` as this field will change after the EIP is attached"
value = try(
aws_instance.this.public_ip,
null,
)
}
output "private_ip" {
description = "The private IP address assigned to the instance"
value = try(
aws_instance.this.private_ip,
null,
)
}
output "ipv6_addresses" {
description = "The IPv6 address assigned to the instance, if applicable"
value = try(
aws_instance.this.ipv6_addresses,
[],
)
}

View File

@@ -0,0 +1,19 @@
#!/bin/bash
set -xe
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce
sudo usermod -aG docker ubuntu
docker run \
--restart=unless-stopped \
--name=httpbin \
-p "80:80" \
kong/httpbin

View File

@@ -0,0 +1,82 @@
variable "ami" {
type = string
description = "AMI ID for the EC2 instance"
default = "ami-0b2a9065573b0a9c9" # Ubuntu 22.04 in us-east-1
validation {
condition = length(var.ami) > 4 && substr(var.ami, 0, 4) == "ami-"
error_message = "Please provide a valid value for variable AMI."
}
}
variable "associate_public_ip_address" {
description = "Whether to associate a public IP address with an instance in a VPC"
type = bool
default = false
}
variable "instance_type" {
description = "The type of instance to start"
type = string
default = "t3.micro"
}
variable "instance_tags" {
description = "Additional tags for the instance"
type = map(string)
default = {}
}
variable "ipv6_addresses" {
description = "Specify one or more IPv6 addresses from the range of the subnet to associate with the primary network interface"
type = list(string)
default = null
}
variable "key_name" {
description = "Key name of the Key Pair to use for the instance; which can be managed using the `aws_key_pair` resource"
type = string
default = null
}
variable "monitoring" {
description = "If true, the launched EC2 instance will have detailed monitoring enabled"
type = bool
default = false
}
variable "name" {
description = "Name to be used on EC2 instance created"
type = string
default = ""
}
variable "private_ip" {
description = "Private IP address to associate with the instance in a VPC"
type = string
default = null
}
variable "root_block_device" {
description = "Customize details about the root block device of the instance. See Block Devices below for details"
type = list(any)
default = []
}
variable "subnet_id" {
description = "The VPC Subnet ID to launch in"
type = string
default = null
}
variable "tags" {
description = "A mapping of tags to assign to the resource"
type = map(string)
default = {}
}
variable "vpc_security_group_ids" {
description = "A list of security group IDs to associate with"
type = list(string)
default = null
}

View File

@@ -0,0 +1,19 @@
resource "aws_instance" "this" {
ami = var.ami
instance_type = var.instance_type
monitoring = var.monitoring
subnet_id = var.subnet_id
vpc_security_group_ids = var.vpc_security_group_ids
associate_public_ip_address = var.associate_public_ip_address
private_ip = var.private_ip
key_name = var.key_name
user_data = file("${path.module}/scripts/setup.sh")
root_block_device {
volume_type = "gp3"
volume_size = 20
}
tags = merge({ "Name" = var.name }, var.instance_tags, var.tags)
}

View File

@@ -0,0 +1,55 @@
output "id" {
description = "The ID of the instance"
value = try(
aws_instance.this.id,
null,
)
}
output "arn" {
description = "The ARN of the instance"
value = try(
aws_instance.this.arn,
null,
)
}
output "instance_state" {
description = "The state of the instance"
value = try(
aws_instance.this.instance_state,
null,
)
}
output "primary_network_interface_id" {
description = "The ID of the instance's primary network interface"
value = try(
aws_instance.this.primary_network_interface_id,
null,
)
}
output "public_ip" {
description = "The public IP address assigned to the instance, if applicable. NOTE: If you are using an aws_eip with your instance, you should refer to the EIP's address directly and not use `public_ip` as this field will change after the EIP is attached"
value = try(
aws_instance.this.public_ip,
null,
)
}
output "private_ip" {
description = "The private IP address assigned to the instance"
value = try(
aws_instance.this.private_ip,
null,
)
}
output "ipv6_addresses" {
description = "The IPv6 address assigned to the instance, if applicable"
value = try(
aws_instance.this.ipv6_addresses,
[],
)
}

View File

@@ -0,0 +1,20 @@
#!/bin/bash
set -xe
sudo apt-get update
sudo apt-get install -y iperf3
sudo tee -a /etc/systemd/system/iperf3.service << EOF
[Unit]
Description=iperf3 server
After=syslog.target network.target auditd.service
[Service]
ExecStart=/usr/bin/iperf3 -s
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable --now iperf3

View File

@@ -0,0 +1,82 @@
variable "ami" {
type = string
description = "AMI ID for the EC2 instance"
default = "ami-0b2a9065573b0a9c9" # Ubuntu 22.04 in us-east-1
validation {
condition = length(var.ami) > 4 && substr(var.ami, 0, 4) == "ami-"
error_message = "Please provide a valid value for variable AMI."
}
}
variable "associate_public_ip_address" {
description = "Whether to associate a public IP address with an instance in a VPC"
type = bool
default = false
}
variable "instance_type" {
description = "The type of instance to start"
type = string
default = "t3.micro"
}
variable "instance_tags" {
description = "Additional tags for the instance"
type = map(string)
default = {}
}
variable "ipv6_addresses" {
description = "Specify one or more IPv6 addresses from the range of the subnet to associate with the primary network interface"
type = list(string)
default = null
}
variable "key_name" {
description = "Key name of the Key Pair to use for the instance; which can be managed using the `aws_key_pair` resource"
type = string
default = null
}
variable "monitoring" {
description = "If true, the launched EC2 instance will have detailed monitoring enabled"
type = bool
default = false
}
variable "name" {
description = "Name to be used on EC2 instance created"
type = string
default = ""
}
variable "private_ip" {
description = "Private IP address to associate with the instance in a VPC"
type = string
default = null
}
variable "root_block_device" {
description = "Customize details about the root block device of the instance. See Block Devices below for details"
type = list(any)
default = []
}
variable "subnet_id" {
description = "The VPC Subnet ID to launch in"
type = string
default = null
}
variable "tags" {
description = "A mapping of tags to assign to the resource"
type = map(string)
default = {}
}
variable "vpc_security_group_ids" {
description = "A list of security group IDs to associate with"
type = list(string)
default = null
}

View File

@@ -0,0 +1,19 @@
resource "aws_instance" "this" {
ami = var.ami
instance_type = var.instance_type
monitoring = var.monitoring
subnet_id = var.subnet_id
vpc_security_group_ids = var.vpc_security_group_ids
associate_public_ip_address = var.associate_public_ip_address
source_dest_check = false
key_name = var.key_name
user_data = file("${path.module}/scripts/setup.sh")
root_block_device {
volume_type = "gp3"
volume_size = 15
}
tags = merge({ "Name" = var.name }, var.instance_tags, var.tags)
}

View File

@@ -0,0 +1,55 @@
output "id" {
description = "The ID of the instance"
value = try(
aws_instance.this.id,
null,
)
}
output "arn" {
description = "The ARN of the instance"
value = try(
aws_instance.this.arn,
null,
)
}
output "instance_state" {
description = "The state of the instance"
value = try(
aws_instance.this.instance_state,
null,
)
}
output "primary_network_interface_id" {
description = "The ID of the instance's primary network interface"
value = try(
aws_instance.this.primary_network_interface_id,
null,
)
}
output "public_ip" {
description = "The public IP address assigned to the instance, if applicable. NOTE: If you are using an aws_eip with your instance, you should refer to the EIP's address directly and not use `public_ip` as this field will change after the EIP is attached"
value = try(
aws_instance.this.public_ip,
null,
)
}
output "private_ip" {
description = "The private IP address assigned to the instance"
value = try(
aws_instance.this.private_ip,
null,
)
}
output "ipv6_addresses" {
description = "The IPv6 address assigned to the instance, if applicable"
value = try(
aws_instance.this.ipv6_addresses,
[],
)
}

View File

@@ -0,0 +1,18 @@
#!/bin/bash
set -xe
sudo apt-get update
# Enable IP forwarding
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
# Setup iptables NAT
sudo iptables -t nat -A POSTROUTING -o ens5 -s 0.0.0.0/0 -j MASQUERADE
# Save iptables rules in case of reboot
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y iptables-persistent
sudo systemctl enable --now netfilter-persistent.service
sudo mkdir -p /etc/iptables
sudo /usr/bin/iptables-save | sudo tee -a /etc/iptables/rules.v4

View File

@@ -0,0 +1,82 @@
variable "ami" {
type = string
description = "AMI ID for the EC2 instance"
default = "ami-0b2a9065573b0a9c9" # Ubuntu 22.04 in us-east-1
validation {
condition = length(var.ami) > 4 && substr(var.ami, 0, 4) == "ami-"
error_message = "Please provide a valid value for variable AMI."
}
}
variable "associate_public_ip_address" {
description = "Whether to associate a public IP address with an instance in a VPC"
type = bool
default = true
}
variable "instance_type" {
description = "The type of instance to start"
type = string
default = "t3.micro"
}
variable "instance_tags" {
description = "Additional tags for the instance"
type = map(string)
default = {}
}
variable "ipv6_addresses" {
description = "Specify one or more IPv6 addresses from the range of the subnet to associate with the primary network interface"
type = list(string)
default = null
}
variable "key_name" {
description = "Key name of the Key Pair to use for the instance; which can be managed using the `aws_key_pair` resource"
type = string
default = null
}
variable "monitoring" {
description = "If true, the launched EC2 instance will have detailed monitoring enabled"
type = bool
default = false
}
variable "name" {
description = "Name to be used on EC2 instance created"
type = string
default = ""
}
variable "private_ip" {
description = "Private IP address to associate with the instance in a VPC"
type = string
default = null
}
variable "root_block_device" {
description = "Customize details about the root block device of the instance. See Block Devices below for details"
type = list(any)
default = []
}
variable "subnet_id" {
description = "The VPC Subnet ID to launch in"
type = string
default = null
}
variable "tags" {
description = "A mapping of tags to assign to the resource"
type = map(string)
default = {}
}
variable "vpc_security_group_ids" {
description = "A list of security group IDs to associate with"
type = list(string)
default = null
}