README.md

ec2-node

Creates EC2 instances with IMDSv2, encrypted root volumes, and optional key pairs.

Provisions one or more EC2 instances with IMDSv2 enforced, encrypted root and data EBS volumes, and optional detailed monitoring. Key pairs are created and associated when provided. ami_id is passed in explicitly per instance to keep builds deterministic — there is no AMI lookup or filtering in this module. Instance names are derived from name_prefix, the resolved AWS availability-zone ID, and each instance's name_suffix.

Primary and secondary private IPs can be set explicitly. Secondary IPs are attached to the instance's primary ENI and can be added or changed on existing instances without replacing them, subject to the max IP count for the chosen instance type.

Usage

Basic instance

module "ec2" {
  source  = "hcassc.jfrog.io/iac-tf-modules-virtual__compute/ec2-node/aws"
  version = "0.6.0"

  name_prefix = "prod"

  instances = {
    bastion = {
      name_suffix           = "bastion"
      ami_id                = "ami-0123456789abcdef0"
      instance_type         = "t3.micro"
      subnet_id             = module.vpc.public_subnet_ids["a"]
      security_group_ids    = [module.security_groups.ids["bastion"]]
      iam_instance_profile  = module.iam_role.instance_profile_name
      associate_public_ip   = true

      root_volume = {
        size = 20
      }
    }
  }
}

Fixed primary and secondary private IPs

Useful when downstream systems (DNS, firewall rules, peer configs) need to reference stable addresses. Note the instance type's max private-IP-per-ENI limit caps how many secondary IPs you can request — a t3.micro, for example, allows only one secondary IP in addition to the primary.

module "ec2" {
  source  = "hcassc.jfrog.io/iac-tf-modules-virtual__compute/ec2-node/aws"
  version = "0.6.0"

  name_prefix = "prod"

  instances = {
    app-node = {
      name_suffix            = "app-node"
      ami_id                 = "ami-0123456789abcdef0"
      instance_type          = "t3.medium"
      subnet_id              = module.vpc.private_subnet_ids["a"]
      security_group_ids     = [module.security_groups.ids["app"]]
      primary_private_ip     = "10.0.1.10"
      secondary_private_ips  = ["10.0.1.11", "10.0.1.12"]

      root_volume = {
        size = 20
      }
    }
  }
}

Multiple instances with data volumes and key pair

module "ec2" {
  source  = "hcassc.jfrog.io/iac-tf-modules-virtual__compute/ec2-node/aws"
  version = "0.6.0"

  name_prefix = "prod"

  key_pairs = {
    ops = {
      name       = "ops-team"
      public_key = file("${path.module}/keys/ops.pub")
    }
  }

  instances = {
    db-primary = {
      name_suffix         = "db-primary"
      ami_id              = "ami-0123456789abcdef0"
      instance_type       = "r6g.large"
      subnet_id           = module.vpc.private_subnet_ids["a"]
      security_group_ids  = [module.security_groups.ids["db"]]
      key_name            = "ops"
      primary_private_ip  = "10.0.2.10"

      root_volume = {
        size = 20
      }

      data_volumes = [
        {
          device_name = "/dev/xvdf"
          volume_size = 200
          volume_type = "gp3"
          iops        = 6000
          throughput  = 250
        }
      ]

      instance_tags = {
        role = "database"
      }
    }

    db-replica = {
      name_suffix         = "db-replica"
      ami_id              = "ami-0123456789abcdef0"
      instance_type       = "r6g.large"
      subnet_id           = module.vpc.private_subnet_ids["b"]
      security_group_ids  = [module.security_groups.ids["db"]]
      key_name            = "ops"
      primary_private_ip  = "10.0.3.10"

      root_volume = {
        size = 20
      }

      data_volumes = [
        {
          device_name = "/dev/xvdf"
          volume_size = 200
        }
      ]

      instance_tags = {
        role = "database-replica"
      }
    }
  }

  tags = {
    environment = "prod"
    team        = "platform"
  }
}

Requirements

Name Version
terraform >= 1.15.0, < 2.0.0
aws >= 6.0, < 7.0

Providers

Name Version
aws >= 6.0, < 7.0

Resources

Name Type
aws_ebs_volume.data resource
aws_instance.this resource
aws_key_pair.this resource
aws_volume_attachment.data resource

Inputs

Name Description Type Default Required
name_prefix Prefix prepended to resource names created by this module string n/a yes
instances Map of EC2 instance definitions.- ami_id is passed in explicitly to ensure deterministic builds.- name_suffix is appended to resources to ensure standardized names. map(object({ name_suffix = string ami_id = string instance_type = string subnet_id = string security_group_ids = list(string) iam_instance_profile = optional(string) key_name = optional(string) associate_public_ip = optional(bool, false) ebs_optimized = optional(bool, true) monitoring = optional(bool, false) placement_group = optional(string) tenancy = optional(string, "default") primary_private_ip = optional(string) secondary_private_ips = optional(list(string), []) # User data — mutually exclusive user_data = optional(string) user_data_base64 = optional(string) # IMDSv2 — tokens required by default, hop limit 1 unless overridden (e.g. ECS needs 2) metadata_http_tokens = optional(string, "required") metadata_hop_limit = optional(number, 1) # Root volume root_volume = object({ size = number type = optional(string, "gp3") kms_key_id = optional(string) delete_on_termination = optional(bool, true) tags = optional(map(string), {}) }) # Additional EBS data volumes data_volumes = optional(list(object({ device_name = string volume_size = number volume_type = optional(string, "gp3") kms_key_id = optional(string) delete_on_termination = optional(bool, true) iops = optional(number) throughput = optional(number) tags = optional(map(string), {}) })), []) # Per-instance tags — overrides module level tags instance_tags = optional(map(string), {}) })) {} no
key_pairs Map of key pairs to register in AWS EC2.Each entry requires a name and an OpenSSH-format public key stringwhich may be sourced from a secret store or a TLS provider. map(object({ name = string public_key = string })) {} no
tags Tags applied to all resources in this module. map(string) {} no

Outputs

Name Description
ami_ids Map of instance keys to the AMI ID in use.
data_volume_attachments Map of data volume keys to attached volume IDs.
data_volume_ids Map of data volume keys to EBS volume IDs.
instance_arns Map of instance keys to instance ARNs.
instance_ids Map of instance keys to instance IDs.
key_pair_fingerprints Map of key pair keys to fingerprints.
key_pair_ids Map of key pair keys to AWS key pair IDs.
network_interface_ids Map of instance keys to primary network interface IDs (for manual ENI IP tests).
primary_private_ips Map of instance keys to primary private IP addresses.
private_dns Map of instance keys to private DNS names.
public_ips Map of instance keys to public IP addresses. Null if no public IP assigned.
secondary_private_ips Map of instance keys to their secondary private IP addresses.