Skip to content

Module Structure

terraform-aws-arc-ec2

Latest Release Last Updated Terraform GitHub Actions

Quality gate

Known Vulnerabilities

Overview

For more information about this repository and its usage, please see Terraform AWS ARC GitHub EC2 Module Usage Guide.

Introduction

SourceFuse's AWS Reference Architecture (ARC) Terraform module simplifies the creation and management of essential AWS infrastructure components. It is designed to provision and configure the following resources:

  1. EC2 Instances: Deploys and manages one or more EC2 instances, with customizable instance types, AMIs, key pairs, and network configurations.
  2. Volumes: Attaches EBS volumes to the EC2 instances for persistent storage, with options to specify volume size, type, and IOPS.
  3. Load Balancer: Creates an Elastic Load Balancer (ELB) to distribute incoming traffic across multiple EC2 instances, ensuring high availability and reliability. It supports both application (ALB) and network load balancers (NLB).
  4. Security Groups: Defines and manages security groups to control inbound and outbound traffic to the EC2 instances and load balancers, enhancing network security.
  5. EC2 Instance Profile: Creates and attaches an instance profile with IAM roles and policies to the EC2 instances, allowing them to interact with other AWS services securely.

Prerequisites

Before using this module, ensure you have the following:

  • AWS credentials configured.
  • Terraform installed.
  • A working knowledge of Terraform.

Getting Started

  1. Define the Module

Initially, it's essential to define a Terraform module, which is organized as a distinct directory encompassing Terraform configuration files. Within this module directory, input variables and output values must be defined in the variables.tf and outputs.tf files, respectively. The following illustrates an example directory structure:

1
2
3
4
ec2/
|-- main.tf
|-- variables.tf
|-- outputs.tf
  1. Define Input Variables

Inside the variables.tf or in *.tfvars file, you should define values for the variables that the module requires.

  1. Use the Module in Your Main Configuration In your main Terraform configuration file (e.g., main.tf), you can use the module. Specify the source of the module, and version, For Example
module "ec2" {
  source = "sourcefuse/arc-ec2/aws"
  version = "0.0.1"

  name                  = "${var.namespace}-${var.environment}-test"
  instance_type         = "t3.small"
  ami_id                = data.aws_ami.amazon_linux.id
  vpc_id                = data.aws_vpc.this.id
  subnet_id             = "subnet-066d0c78479b72e77"
  private_ip            = "10.12.134.2"
  instance_profile_data = local.instance_profile_data
  security_group_data   = local.security_group_data

  root_block_device_data = {
    volume_size = 10
    volume_type = "gp3"
  }
  additional_ebs_volumes = local.additional_ebs_volumes

  load_balancer_data = local.load_balancer_data
  target_groups      = local.target_groups

  tags = module.tags.tags
}
  1. Output Values

Inside the outputs.tf file of the module, you can define output values that can be referenced in the main configuration. For example:

output "instance_id" {
  description = "Instance ID"
  value       = module.ec2_instances.id
}

output "instance_arn" {
  description = "Instance ARN"
  value       = module.ec2_instances.arn
}

output "load_balancer_arn" {
  value = module.ec2_instances.load_balancer_arn
}

output "listener_arn" {
  description = "Listener ARN"
  value       = module.ec2_instances.listener_arn
}

output "target_group_arn" {
  description = "Target Group ARN"
  value       = module.ec2_instances.target_group_arn
}
  1. .tfvars

Inside the .tfvars file of the module, you can provide desired values that can be referenced in the main configuration. For example:

Edit the locals.tf file and provide desired values.

security_group_data - Security Group details for EC2 instance

instance_profile_data - EC2 instance profile

additional_ebs_volumes - Additional EBS volume details

target_groups - Target Group details for Load balancer

load_balancer_data - Details to create Load balancer

locals {

  security_group_data = {
    create             = true
    name               = "${var.namespace}-${var.environment}-sg"
    security_group_ids = []
    ingress_rules = [{
      description = "Allow SSH"
      from_port   = 22
      to_port     = 22
      protocol    = "tcp"
      cidr_blocks = [data.aws_vpc.this.cidr_block]
    }]
    egress_rules = [{
      description = "Allow All outbound calls"
      from_port   = 0
      to_port     = 0
      protocol    = -1
      cidr_blocks = ["0.0.0.0/0"]
    }]
  }

  instance_profile_data = {
    name   = "${var.namespace}-${var.environment}-test-profile"
    create = true
    policy_documents = [
      {
        name   = "s3-read"
        policy = data.aws_iam_policy_document.s3_read_list.json
      }
    ]
  }

  additional_ebs_volumes = {
    "vol-1" = {
      name        = "vol-1"
      device_name = "/dev/sdb"
      encrypted   = true
      size = 20
      type = "gp3"
  } }

  target_groups = {
    "group-1" = {
      port     = 80
      protocol = "HTTP"

      health_check = {
        path     = "/"
        timeout  = 20
        interval = 30
      }
      listeners = [
        {
          port       = "80"
          protocol   = "HTTP"
          ssl_policy = null

          default_action = {
            type = "redirect"
            redirect = {
              port        = 443
              protocol    = "HTTPS"
              status_code = "HTTP_301"
            }
          }

        },
        {
          port            = "443"
          protocol        = "HTTPS"
          ssl_policy      = "ELBSecurityPolicy-TLS13-1-2-2021-06"
          certificate_arn = "arn:aws:acm:us-east-1:xxxx:certificate/xx-xx-xx-xx-xx"

          default_action = {
            type = "forward"
          }
        }
      ]
      target = {
        port = 80
      }
    }
  }

  load_balancer_data = {
    create  = true
    name    = "${var.namespace}-${var.environment}-alb"
    subnets = data.aws_subnets.public.ids
  }

}

First Time Usage

uncomment the backend block in main.tf

terraform init -backend-config=config.dev.hcl
If testing locally, terraform init should be fine

Create a dev workspace

terraform workspace new dev

Plan Terraform

terraform plan -var-file terraform.tfvars

Apply Terraform

terraform apply -var-file terraform.tfvars

Production Setup

terraform init -backend-config=config.prod.hcl

Create a prod workspace

terraform workspace new prod

Plan Terraform

terraform plan -var-file prod.tfvars

Apply Terraform

terraform apply -var-file prod.tfvars  

Cleanup

Destroy Terraform

terraform destroy -var-file dev.tfvars

Requirements

Name Version
terraform >= 1.5.0
aws ~> 5.0

Providers

Name Version
aws 5.57.0

Modules

Name Source Version
load_balancer ./modules/alb n/a

Resources

Name Type
aws_ebs_volume.this resource
aws_eip.this resource
aws_iam_instance_profile.this resource
aws_iam_role.this resource
aws_instance.this resource
aws_security_group.this resource
aws_volume_attachment.this resource

Inputs

Name Description Type Default Required
additional_ebs_volumes (optional) ebs_block_device block supports the following:
name - (Optional) Name of the volume
delete_on_termination - (Optional) Whether the volume should be destroyed on instance termination. Defaults to true.
device_name - (Required) Name of the device to mount.
encrypted - (Optional) Enables EBS encryption on the volume. Defaults to false. Cannot be used with snapshot_id. Must be configured to perform drift detection.
iops - (Optional) Amount of provisioned IOPS. Only valid for volume_type of io1, io2 or gp3.
kms_key_id - (Optional) Amazon Resource Name (ARN) of the KMS Key to use when encrypting the volume. Must be configured to perform drift detection.
snapshot_id - (Optional) Snapshot ID to mount.
tags - (Optional) Map of tags to assign to the device.
throughput - (Optional) Throughput to provision for a volume in mebibytes per second (MiB/s). This is only valid for volume_type of gp3.
volume_size - (Optional) Size of the volume in gibibytes (GiB).
volume_type - (Optional) Type of volume. Valid values include standard, gp2, gp3, io1, io2, sc1, or st1. Defaults to gp2.

Device name : https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/device_naming.html
map(object({
name = optional(string, null)
delete_on_termination = optional(bool, true)
device_name = string
encrypted = optional(bool, false)
iops = optional(string, null)
kms_key_id = optional(string, null)
throughput = optional(string, null)
size = number
type = optional(string, "gp2")

}))
{} no
ami_id The AMI ID for the EC2 instance. string n/a yes
assign_eip (optional) Whether to assign Elastic IP address, note associate_public_ip_address has to be enabled bool false no
associate_public_ip_address Associate a public IP address with the instance. bool false no
ebs_optimized (optional) If true, the launched EC2 instance will be EBS-optimized. Note that if this is not set on an instance type that is optimized by default then this will show as disabled but if the instance type is optimized by default then there is no need to set this and there is no effect to disabling it. bool false no
enable_detailed_monitoring (optional) Whether to enable detailed monitoring bool false no
enable_stop_protection (optional) If true, enables EC2 Instance Stop Protection. bool false no
enable_termination_protection (optional) If true, enables EC2 Instance Termination Protection. bool false no
instance_metadata_options The metadata_options block supports the following:

http_endpoint - (Optional) Whether the metadata service is available. Valid values include enabled or disabled. Defaults to enabled.
http_protocol_ipv6 - (Optional) Whether the IPv6 endpoint for the instance metadata service is enabled. Defaults to disabled.
http_put_response_hop_limit - (Optional) Desired HTTP PUT response hop limit for instance metadata requests. The larger the number, the further instance metadata requests can travel. Valid values are integer from 1 to 64. Defaults to 1.
http_tokens - (Optional) Whether or not the metadata service requires session tokens, also referred to as Instance Metadata Service Version 2 (IMDSv2). Valid values include optional or required. Defaults to optional.
instance_metadata_tags - (Optional) Enables or disables access to instance tags from the instance metadata service. Valid values include enabled or disabled. Defaults to disabled.
object({
http_endpoint = optional(string, "enabled")
http_protocol_ipv6 = optional(string, "disabled")
http_put_response_hop_limit = optional(number, 1)
http_tokens = optional(string, "required")
instance_metadata_tags = optional(string, "disabled")
})
{
"http_endpoint": "enabled",
"http_protocol_ipv6": "disabled",
"http_put_response_hop_limit": 1,
"http_tokens": "required",
"instance_metadata_tags": "disabled"
}
no
instance_profile_data (optional) IAM Instance Profile to launch the instance with. Specified as the name of the Instance Profile.
object({
name = optional(string, null)
create = optional(bool, false)
policy_documents = optional(list(object({
name = string
policy = string
})), [])
managed_policy_arns = optional(list(string), [])
})
{
"create": false,
"managed_policy_arns": [],
"name": null,
"policy_documents": []
}
no
instance_type Instance type for EC2 instance string n/a yes
load_balancer_create_timeout Timeout value when creating the ALB. string "10m" no
load_balancer_data (optional) describe your variable
object({
create = bool
name = string
internal = optional(bool, false)
load_balancer_type = optional(string, "application")
subnets = list(string)
enable_deletion_protection = optional(bool, false)
idle_timeout = optional(number, 60)
enable_cross_zone_load_balancing = optional(bool, false)
enable_http2 = optional(bool, true)
enable_tls_version_and_cipher_suite_headers = optional(bool, false)
enable_xff_client_port = optional(bool, false)
preserve_host_header = optional(bool, true)
enable_waf_fail_open = optional(bool, false)
desync_mitigation_mode = optional(string, "defensive")
xff_header_processing_mode = optional(string, "append")
ip_address_type = optional(string, "ipv4")
drop_invalid_header_fields = optional(bool, true)
})
{
"create": false,
"name": null,
"subnets": []
}
no
load_balancer_delete_timeout Timeout value when deleting the ALB. string "10m" no
load_balancer_security_group_data (optional) Security Group data for Loadbalancer
object({
create = optional(bool, false)
name = optional(string, null)
description = optional(string, null)
security_group_ids = optional(list(string))
ingress_rules = optional(list(object({
description = optional(string, null)
from_port = string
to_port = string
protocol = string
cidr_blocks = list(string)
security_groups = optional(list(string), [])
ipv6_cidr_blocks = optional(list(string), [])
})))
egress_rules = optional(list(object({
description = optional(string, null)
from_port = string
to_port = string
protocol = string
cidr_blocks = list(string)
security_groups = optional(list(string), [])
ipv6_cidr_blocks = optional(list(string), [])
})))
})
{
"create": true,
"egress_rules": [
{
"cidr_blocks": [
"0.0.0.0/0"
],
"description": "Allow All outbound calls",
"from_port": 0,
"protocol": -1,
"to_port": 0
}
],
"ingress_rules": [
{
"cidr_blocks": [
"0.0.0.0/0"
],
"description": "Allow http port",
"from_port": 80,
"protocol": "tcp",
"to_port": 80
},
{
"cidr_blocks": [
"0.0.0.0/0"
],
"description": "Allow https port",
"from_port": 443,
"protocol": "tcp",
"to_port": 443
}
]
}
no
load_balancer_update_timeout Timeout value when updating the ALB. string "10m" no
name Name of the instance string n/a yes
private_ip (optional) Private IP for the instance string null no
root_block_device_data The root_block_device block supports the following:

delete_on_termination - (Optional) Whether the volume should be destroyed on instance termination. Defaults to true.
encrypted - (Optional) Whether to enable volume encryption. Defaults to false. Must be configured to perform drift detection.
iops - (Optional) Amount of provisioned IOPS. Only valid for volume_type of io1, io2 or gp3.
kms_key_id - (Optional) Amazon Resource Name (ARN) of the KMS Key to use when encrypting the volume. Must be configured to perform drift detection.
tags - (Optional) Map of tags to assign to the device.
throughput - (Optional) Throughput to provision for a volume in mebibytes per second (MiB/s). This is only valid for volume_type of gp3.
volume_size - (Optional) Size of the volume in gibibytes (GiB).
volume_type - (Optional) Type of volume. Valid values include standard, gp2, gp3, io1, io2, sc1, or st1. Defaults to the volume type that the AMI uses.
object({
delete_on_termination = optional(bool, true)
encrypted = optional(bool, true)
iops = optional(string, null)
kms_key_id = optional(string, null)
throughput = optional(number, null)
volume_size = string
volume_type = string
})
n/a yes
security_group_data (optional) Security Group data
object({
create = optional(bool, false)
name = optional(string, null)
description = optional(string, null)
security_group_ids = optional(list(string))
ingress_rules = optional(list(object({
description = optional(string, null)
from_port = string
to_port = string
protocol = string
cidr_blocks = list(string)
security_groups = optional(list(string), [])
ipv6_cidr_blocks = optional(list(string), [])
})))
egress_rules = optional(list(object({
description = optional(string, null)
from_port = string
to_port = string
protocol = string
cidr_blocks = list(string)
security_groups = optional(list(string), [])
ipv6_cidr_blocks = optional(list(string), [])
})))
})
n/a yes
ssh_key_pair (optional) SSH Key Pair for EC2 instance string null no
subnet_id Subnet ID on which EC2 instance has to be created string n/a yes
tags (optional) Tags for EC2 instance map(string) {} no
target_groups Target Group details
map(object({
port = number
protocol = string

health_check = object({
enabled = optional(bool, true)
healthy_threshold = optional(number, 3)
unhealthy_threshold = optional(number, 3)
path = optional(string, "/")
timeout = optional(number, 20)
interval = optional(number, 30)
matcher = optional(string, "200")
})

listeners = list(object({
port = string
protocol = string
ssl_policy = optional(string, "ELBSecurityPolicy-TLS13-1-2-2021-06")
certificate_arn = optional(string, null)

default_action = object({
type = string // valid values : redirect,fixed-response,forward if forward , then target_group_arn = aws_lb_target_group.this.arn
redirect = optional(object({
port = number
protocol = string
status_code = string
}), null)

fixed_response = optional(object({
content_type = string
message_body = string
status_code = optional(string, "200")
}), null)

}) }))

target = object({
port = number
})

}))
{} no
user_data (optional) User data to provide when launching the instance. Do not pass gzip-compressed data via this argument; see user_data_base64 instead. Updates to this field will trigger a stop/start of the EC2 instance by default. string null no
user_data_base64 (optional) 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
vpc_id The VPC ID where resources will be deployed. string n/a yes

Outputs

Name Description
arn Instance ID
id Instance ID
listener_arn Listener ARN
load_balancer_arn n/a
target_group_arn Target Group ARN

Git commits

while Contributing or doing git commit please specify the breaking change in your commit message whether its major,minor or patch

For Example

git commit -m "your commit message #major"
By specifying this , it will bump the version and if you dont specify this in your commit message then by default it will consider patch and will bump that accordingly

Development

Prerequisites

Configurations

  • Configure pre-commit hooks
    pre-commit install
    

Tests

  • Tests are available in test directory
  • Configure the dependencies
    1
    2
    3
    cd test/
    go mod init github.com/sourcefuse/terraform-aws-refarch-<module_name>
    go get github.com/gruntwork-io/terratest/modules/terraform
    
  • Now execute the test
    go test -timeout  30m
    

Authors

This project is authored by: - SourceFuse