mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 10:18:54 +00:00
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:
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
18
terraform/modules/aws/bastion/main.tf
Normal file
18
terraform/modules/aws/bastion/main.tf
Normal 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)
|
||||
}
|
||||
55
terraform/modules/aws/bastion/outputs.tf
Normal file
55
terraform/modules/aws/bastion/outputs.tf
Normal 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,
|
||||
[],
|
||||
)
|
||||
}
|
||||
34
terraform/modules/aws/bastion/scripts/setup.sh
Normal file
34
terraform/modules/aws/bastion/scripts/setup.sh
Normal 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}"
|
||||
82
terraform/modules/aws/bastion/variables.tf
Normal file
82
terraform/modules/aws/bastion/variables.tf
Normal 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
|
||||
}
|
||||
51
terraform/modules/aws/gateway/main.tf
Normal file
51
terraform/modules/aws/gateway/main.tf
Normal 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)
|
||||
}
|
||||
55
terraform/modules/aws/gateway/outputs.tf
Normal file
55
terraform/modules/aws/gateway/outputs.tf
Normal 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,
|
||||
[],
|
||||
)
|
||||
}
|
||||
40
terraform/modules/aws/gateway/templates/cloud-init.yaml
Normal file
40
terraform/modules/aws/gateway/templates/cloud-init.yaml
Normal 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
|
||||
150
terraform/modules/aws/gateway/variables.tf
Normal file
150
terraform/modules/aws/gateway/variables.tf
Normal 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
|
||||
}
|
||||
19
terraform/modules/aws/httpbin/main.tf
Normal file
19
terraform/modules/aws/httpbin/main.tf
Normal 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)
|
||||
}
|
||||
55
terraform/modules/aws/httpbin/outputs.tf
Normal file
55
terraform/modules/aws/httpbin/outputs.tf
Normal 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,
|
||||
[],
|
||||
)
|
||||
}
|
||||
19
terraform/modules/aws/httpbin/scripts/setup.sh
Normal file
19
terraform/modules/aws/httpbin/scripts/setup.sh
Normal 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
|
||||
82
terraform/modules/aws/httpbin/variables.tf
Normal file
82
terraform/modules/aws/httpbin/variables.tf
Normal 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
|
||||
}
|
||||
19
terraform/modules/aws/iperf/main.tf
Normal file
19
terraform/modules/aws/iperf/main.tf
Normal 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)
|
||||
}
|
||||
55
terraform/modules/aws/iperf/outputs.tf
Normal file
55
terraform/modules/aws/iperf/outputs.tf
Normal 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,
|
||||
[],
|
||||
)
|
||||
}
|
||||
20
terraform/modules/aws/iperf/scripts/setup.sh
Normal file
20
terraform/modules/aws/iperf/scripts/setup.sh
Normal 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
|
||||
82
terraform/modules/aws/iperf/variables.tf
Normal file
82
terraform/modules/aws/iperf/variables.tf
Normal 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
|
||||
}
|
||||
19
terraform/modules/aws/nat/main.tf
Normal file
19
terraform/modules/aws/nat/main.tf
Normal 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)
|
||||
}
|
||||
55
terraform/modules/aws/nat/outputs.tf
Normal file
55
terraform/modules/aws/nat/outputs.tf
Normal 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,
|
||||
[],
|
||||
)
|
||||
}
|
||||
18
terraform/modules/aws/nat/scripts/setup.sh
Normal file
18
terraform/modules/aws/nat/scripts/setup.sh
Normal 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
|
||||
82
terraform/modules/aws/nat/variables.tf
Normal file
82
terraform/modules/aws/nat/variables.tf
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user