Skip to content

Coalfire-CF/terraform-aws-ec2

Repository files navigation

Coalfire

AWS EC2 Terraform Module

Description

The EC2 general purpose module creates an EC2 instance for your project. Configuration for the EC2 instance includes networking, storage, IAM, and tags.

Multiple ENIs

In order to assign multiple ENIs to a single instance using this module, the "instance_count" variable must be set to 1.

Resource List

Resources that are created as a part of this module include:

  • EC2 instance
  • Elastic IP
  • Network interface attachment
  • IAM role
  • IAM instance profile
  • KMS RBAC grant
  • AWS security group
  • Target group attachment

Setup and Usage

This is an example of how to create an EC2 instance using this module, with generic variables.

module "ec2_test" {
  source = "github.com/Coalfire-CF/terraform-aws-ec2"

  name = var.instance_name

  ami               = data.aws_ami.ami.id
  ec2_instance_type = var.instance_size
  instance_count    = var.instance_count

  vpc_id = aws_vpc.main.id
  subnet_ids = var.subnet_ids

  ec2_key_pair    = var.key_name
  ebs_kms_key_arn = data.terraform_remote_state.kms.outputs.ebs_kms_key_arn

  # Storage
  root_volume_size = var.instance_volume_size

  # Security Group Rules
    ingress_rules = {
    "rdp" = {
      ip_protocol = "tcp"
      from_port   = "3389"
      to_port     = "3389"
      cidr_ipv4   = var.cidr_for_remote_access
      description = "RDP"
    }
  }

  egress_rules = {
    "allow_all_egress" = {
      ip_protocol = "-1"
      from_port   = "0"
      to_port     = "0"
      cidr_ipv4   = "0.0.0.0/0"
      description = "Allow all egress"
    }
  }

  # Tagging
  global_tags = {}
}

User Data

  user_data = templatefile("${path.module}/../../shellscripts/linux/ud-os-join-ad.sh", {
    aws_region            = var.aws_region
    domain_name           = local.domain_name
    dom_disname           = local.dom_disname
    ou_env                = var.lin_prod_ou_env
    linux_admins_ad_group = var.linux_admins_ad_group
    domain_join_user_name = var.domain_join_user_name
    sm_djuser_path        = "${var.ad_secrets_path}${var.domain_join_user_name}"
    is_asg                = "false"
  })

Security Groups

Ingress Rules:

ingress_rules = {
    "rdp" = {
      ip_protocol = "tcp"
      from_port   = "3389"
      to_port     = "3389"
      cidr_ipv4   = var.cidr_for_remote_access
      description = "RDP"
    }
  }

Egress Rules:

egress_rules = {
    "allow_all_egress" = {
      ip_protocol = "-1"
      from_port   = "0"
      to_port     = "0"
      cidr_ipv4   = "0.0.0.0/0"
      description = "Allow all egress"
    }
  }

IAM

iam_policies      = [aws_iam_policy.test_policy_1.arn, ...]

Multiple EBS Volumes

The root ebs volume is handled with the below variables:

However, if additional ebs volumes are required, you can use the below variable:

ebs_block_devices = [
    {
      device_name = "/dev/sdf"
      volume_size = "50"
      volume_type = "gp2"
    },
    ...
  ]

Attaching Security Groups or IAM Profile from other instances

The module also supports attaching a security group or IAM Profile from another instance within the same directory. Let's take an example: AD1 creates a security group that can be used by both AD1 and AD2. So, the AD2 module should use the output of the AD1 module to assign the existing security group. Note, AD2 would now have a dependency on AD1. As shown below, the "additional_security_groups" variable can be used for this purpose.

module "ad2" {
 source = "github.com/Coalfire-CF/terraform-aws-ec2"
 name              = "dc2"
 ami               = "ami-XXXXXX"
 ec2_instance_type = "m5a.large"
 ec2_key_pair      = var.key_name
 root_volume_size  = "50"
 subnet_ids        = [data.terraform_remote_state.network-mgmt.outputs.private_subnets[X]]
 vpc_id            = data.terraform_remote_state.network-mgmt.outputs.vpc_id
 private_ip = "${var.ip_network_mgmt}.${var.directory_ip_2}"


 iam_profile = module.ad1.iam_profile
 additional_security_groups = [module.ad1.sg_id]
}

Requirements

Name Version
terraform >=1.5
aws >= 5.15.0, < 6.0

Providers

Name Version
aws >= 5.15.0, < 6.0

Modules

Name Source Version
security_group github.com/Coalfire-CF/terraform-aws-securitygroup b6e9070a3f6201d75160c42a3f649d36cb9b2622

Resources

Name Type
aws_ebs_volume.this resource
aws_eip.eip resource
aws_eip_association.eip_attach resource
aws_iam_instance_profile.this_profile resource
aws_iam_role.this_role resource
aws_iam_role_policy_attachment.iam_policy_attach resource
aws_iam_role_policy_attachment.ssm_role_policy_attach resource
aws_instance.this resource
aws_kms_grant.kms_key_grant resource
aws_lb_target_group_attachment.target_group_attachment resource
aws_network_interface_attachment.eni_attachment resource
aws_network_interface_sg_attachment.additional resource
aws_volume_attachment.this resource
aws_iam_policy.AmazonSSMManagedInstanceCore data source

Inputs

Name Description Type Default Required
add_SSMManagedInstanceCore Whether or not to apply the SSMManagedInstanceCore to the IAM role bool true no
additional_eni_ids This variable allows for an ec2 instance to have multiple ENIs. Instance count must be set to 1 list(string) [] no
additional_security_groups A list of additional security groups to attach to the network interfaces list(string) [] no
ami ID of AMI to use for the instance string n/a yes
associate_eip Whether or not to associate an Elastic IP bool false no
associate_public_ip Whether or not to associate a public IP (not EIP) bool false no
assume_role_policy Policy document allowing Principals to assume this role (e.g. Trust Relationship) string "{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Action\": \"sts:AssumeRole\",\n \"Principal\": {\n \"Service\": \"ec2.amazonaws.com\"\n },\n \"Effect\": \"Allow\",\n \"Sid\": \"\"\n }\n ]\n}\n" no
ebs_kms_key_arn The ARN of the KMS key to encrypt EBS volumes string n/a yes
ebs_optimized Whether or not the instance is ebs optimized bool true no
ebs_volumes A list of maps that must contain device_name (ex. '/dev/sdb') and size (in GB). Optional args include type, throughput, iops, multi_attach_enabled, final_snapshot, snapshot_id, outpost_arn, force_detach, skip_destroy, stop_instance_before_detaching, and tags
list(object({
device_name = string
size = number
type = string
throughput = optional(number)
iops = optional(number)
multi_attach_enabled = optional(bool, false)
final_snapshot = optional(string)
snapshot_id = optional(string)
outpost_arn = optional(string)
force_detach = optional(bool, false)
skip_destroy = optional(bool, false)
stop_instance_before_detaching = optional(bool, false)
tags = optional(map(string), {})
}))
[] no
ec2_instance_type The type of instance to start string n/a yes
ec2_key_pair The key name to use for the instance string n/a yes
egress_rules The list of rules for egress traffic. Required fields for each rule are 'protocol', 'from_port', 'to_port', and at least one of 'cidr_blocks', 'ipv6_cidr_blocks', 'security_groups', 'self', or 'prefix_list_sg'. Optional fields are 'description' and those not used from the previous list
map(object({
cidr_ipv4 = optional(string, null)
cidr_ipv6 = optional(string, null)
description = optional(string, "Managed by Terraform")
from_port = optional(string, null)
ip_protocol = optional(string, null)
prefix_list_id = optional(string, null)
referenced_security_group_id = optional(string, null)
to_port = optional(string, null)
}))
{} no
get_password_data Whether or not to allow retrieval of the local admin password bool false no
global_tags a map of strings that contains global level tags map(string) n/a yes
http_tokens Whether or not the metadata service requires session tokens, required=IMDSv2, optional=IMDSv1 any "required" no
iam_policies A list of the iam policy ARNs to attach to the IAM role list(string) [] no
iam_profile A variable to attach an existing iam profile to the ec2 instance(s) created string "" no
ingress_rules The list of rules for ingress traffic. Required fields for each rule are 'protocol', 'from_port', 'to_port', and at least one of 'cidr_blocks', 'ipv6_cidr_blocks', 'security_groups', 'self', or 'prefix_list_sg'. Optional fields are 'description' and those not used from the previous list
map(object({
cidr_ipv4 = optional(string, null)
cidr_ipv6 = optional(string, null)
description = optional(string, "Managed by Terraform")
from_port = optional(string, null)
ip_protocol = optional(string, null)
prefix_list_id = optional(string, null)
referenced_security_group_id = optional(string, null)
to_port = optional(string, null)
}))
{} no
instance_count Number of instances to launch number 1 no
keys_to_grant A list of kms keys to grant permissions to for the role created. list(string) [] no
name The name of the ec2 instance string n/a yes
private_ip The private ip for the instance string null no
root_volume_size The size of the root ebs volume on the ec2 instances created string n/a yes
root_volume_type The type of the root ebs volume on the ec2 instances created string "gp3" no
sg_description This overwrites the default generated description for the security group string "Managed by Terraform" no
source_dest_check Whether or not source/destination check should be enabled for the primary network interface bool true no
subnet_ids A list of the subnets to be used when provisioning ec2 instances. If instance count is 1, only the first subnet will be used list(string) n/a yes
tags A mapping of tags to assign to the resource map(string) {} no
target_group_arns A list of aws_alb_target_group ARNs, for use with Application Load Balancing list(string) [] no
user_data The User Data script to run string null no
user_data_base64 Can be used instead of user_data to pass base64-encoded binary data directly. Use this instead of user_data whenever the value is not a valid UTF-8 string. For example, gzip-encoded user data must be base64-encoded and passed via this argument to avoid corruption string null no
user_data_replace_on_change When used in combination with user_data or user_data_base64 will trigger a destroy and recreate when set to true. Defaults to false if not set bool null no
vpc_id The id of the vpc where resources are being created string n/a yes

Outputs

Name Description
iam_profile The name of the iam profile created in the module
iam_role_arn The AWS IAM Role arn created
iam_role_name The AWS IAM Role arn created
instance_id The AWS Instance id created
network_interface_id The network interface ID for the AWS instance
primary_private_ip_addresses A list of the primary private IP addesses assigned to the ec2 instance
sg_id The id of the security group created
tags List of tags of instances

Contributing

If you're interested in contributing to our projects, please review the Contributing Guidelines. And send an email to our team to receive a copy of our CLA and start the onboarding process.

License

License

Copyright

Copyright © 2023 Coalfire Systems Inc.