Skip to content

dyegoe/terraform-aws-base-infra

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AWS Base Infra Terraform module

This module itends to deploy some base infrasctructure to AWS.

Usage

module "aws_base_infra" {
  source = "github.com/dyegoe/terraform-aws-base-infra?ref=main"

  project     = "example-project"
  key_name    = "default"
  zone_domain = "example.com"

  vpc = {
    cidr = "192.168.56.0/24"
    azs  = ["a", "b", "c"]
    public_subnets = [
      "192.168.56.0/26",
      "192.168.56.64/26",
      "192.168.56.128/26"
    ]
  }

  default_ingress_sg_rules = {
    internal = {
      from_port = -1,
      to_port = -1,
      ip_protocol = "-1",
      cidr_ipv4 = ["0.0.0.0/0"],
      description = "Allow any from anywhere"
    }
  }

  instances = {
    sample-node0001 = {
      ami_id            = "al2023"
      instance_type     = "t3.nano"
      availability_zone = "a"
      disk_size         = 8
    }
  }
}

Requirements

Name Version
terraform ~> 1.6.0
aws ~> 5.19.0
cloudinit ~> 2.3.2
random ~> 3.5.1

Providers

Name Version
aws ~> 5.19.0
cloudinit ~> 2.3.2
random ~> 3.5.1

Modules

Name Source Version
vpc terraform-aws-modules/vpc/aws ~> 5.1.2

Resources

Name Type
aws_ebs_volume.create resource
aws_ebs_volume.create_prevent_destroy resource
aws_eip.instance resource
aws_eip_association.instance resource
aws_instance.this resource
aws_network_interface.instance resource
aws_route53_record.internal resource
aws_route53_record.public resource
aws_route53_zone.internal resource
aws_security_group.instance resource
aws_security_group.shared resource
aws_volume_attachment.attach resource
aws_volume_attachment.create resource
aws_volume_attachment.create_prevent_destroy resource
aws_vpc_security_group_egress_rule.instance resource
aws_vpc_security_group_egress_rule.shared resource
aws_vpc_security_group_ingress_rule.instance resource
aws_vpc_security_group_ingress_rule.shared resource
random_id.instance_sg resource
random_id.shared_sg resource
aws_default_tags.current data source
aws_region.current data source
aws_route53_zone.current data source
aws_ssm_parameter.al2023 data source
aws_ssm_parameter.amzn2 data source
aws_ssm_parameter.ubuntu2204 data source
aws_subnet.instance data source
cloudinit_config.instance data source

Inputs

Name Description Type Default Required
assign_public_ip Assign public IP to instances. You can override this value for each instance. bool true no
default_egress_sg_rules Default egress rules that could be added to instances security groups
when add_default_egress_sg_rules is set to true for each instance.
map(
object({
from_port = number
to_port = number
ip_protocol = string
cidr_ipv4 = list(string)
description = string
})
)
{
"default_any_to_any": {
"cidr_ipv4": [
"0.0.0.0/0"
],
"description": "Any to Any",
"from_port": -1,
"ip_protocol": "-1",
"to_port": -1
}
}
no
default_ingress_sg_rules Default ingress rules that could be added to instances security groups
when add_default_ingress_sg_rules is set to true for each instance.
map(
object({
from_port = number
to_port = number
ip_protocol = string
cidr_ipv4 = list(string)
description = string
})
)
{} no
instances Map of objects to describe instances.
Map key is used as a name for the instance and must be unique.
Project name will be used as a prefix for the instance name.
The ami_id accepts some pre-defined AMI names: amzn2, al2023, ubuntu2204.
The pre-defined AMI will always get the latest AMI ID for the selected region."
The additional_disks is a map of objects to describe additional disks to create/attach to the instance. The key must be a device name.
The additional_security_groups is a list of security groups to add to the instance. It must be a key from the security_groups variable.
To add the default sg rules to the instance security group, set add_default_egress_sg_rules and/or add_default_ingress_sg_rules to true.
map(object({
ami_id = string
instance_type = string
key_name = optional(string, "")
availability_zone = string
disk_size = optional(number, 8)
assign_public_ip = optional(bool, null)
additional_disks = optional(map(object({
size = number
mount_point = string
mount_mode = optional(string, "0755")
volume_id = optional(string, "")
prevent_destroy = optional(bool, false)
})), {})
additional_security_groups = optional(list(string), [])
add_default_egress_sg_rules = optional(bool, false)
add_default_ingress_sg_rules = optional(bool, false)
egress_sg_rules = optional(map(object({
from_port = number
to_port = number
ip_protocol = string
cidr_ipv4 = list(string)
description = string
})), {})
ingress_sg_rules = optional(map(object({
from_port = number
to_port = number
ip_protocol = string
cidr_ipv4 = list(string)
description = string
})), {})
tags = optional(map(string), {})
}))
n/a yes
key_name Pre-existent key name created on the same region and AWS account that you are creating the resources.
It should match availabilty zones.
string n/a yes
project Project name. It will be used as a prefix for all resources. string n/a yes
security_groups Map of objects to describe security groups and its rules.
map(object({
description = string
ingress = optional(map(object({
from_port = number
to_port = number
ip_protocol = string
cidr_ipv4 = optional(list(string))
self = optional(bool)
description = string
})))
egress = optional(map(object({
from_port = number
to_port = number
ip_protocol = string
cidr_ipv4 = optional(list(string), null)
self = optional(bool, false)
description = string
})))
}))
n/a yes
ssh_port SSH port. number 22 no
volume_type EBS Volume Type. string "gp3" no
vpc A object containing VPC information.
AZs must be a letter that represents the AZ.
For example: ["a", "b", "c"].
Number of public subnets must match the number of availability zones.
Tags are applied to all resources for the VPC.
object({
cidr = string
azs = list(string)
public_subnets = list(string)
public_subnet_tags = optional(map(string), {})
tags = optional(map(string), {})
})
n/a yes
zone_domain A already hosted Route53 domain under the same AWS account that you are creating the resource. string n/a yes

Outputs

Name Description
instances It returns an object of all instances created by the module.
instances_security_group_egress_rules It returns an object of instances security group egress rules.
instances_security_group_ingress_rules It returns an object of instances security group ingress rules.
shared_security_group_egress_rules It returns an object of shared security group egress rules.
shared_security_group_ingress_rules It returns an object of shared security group ingress rules.
shared_security_groups_id It returns an object of shared security groups id.

Components

All componentes names are prefixed with the project name. For example: example-project-vpc.

VPC

It uses terraform-aws-modules/vpc/aws to create a VPC with the following characteristics:

AZs

The AZs must be a letter that represents the AZ. For example: ["a", "b", "c"]. It will be concatenated with the (aws_region.current) region name to create the AZ name. The number of AZs must match the number of public subnets

Security Groups

Shared security groups

Shared security groups are created iterating over the security group variable. The security group name is the project name concatenated with the security group name. For example: example-project-sample-sg.

The map keys are the security group names and must be unique. Each security group has two lists: egress/ingress rules.

The egress/ingress rules are defined by the following syntax:

{
  from_port   = number
  to_port     = number
  ip_protocol = string
  cidr_ipv4   = optional(list(string), null)
  self        = optional(bool, false)
  description = string
}
  • The cidr_ipv4 and self are optional.
  • If self is set to true the security group id will be used as source or destination. Then cidr_ipv4 is not required and will be ignored.
  • If self is set to false the cidr_ipv4 is required and the security group id will be ignored.

Instances egress/ingress rules

Each instace gets a security group. The security group name is the project name concatenated with the instance name and a random string. For example: example-project-sample-node0001-5f3a. The random string is used to avoid security group name conflicts.

The egress/ingress rules are defined by the following variables:

  • var.default_egress_sg_rules
  • var.default_ingress_sg_rules
  • var.instances.egress_sg_rules
  • var.instances.ingress_sg_rules
  • var.instances.add_default_ingress_sg_rules
  • var.instances.add_default_egress_sg_rules

Security group rules have the following syntax:

{
  from_port   = number
  to_port     = number
  ip_protocol = string
  cidr_ipv4   = list(string)
  description = string
}

The default egress/ingress rules could be included to the instances security group if var.instances.add_default_ingress_sg_rules and/or var.instances.add_default_egress_sg_rules is/are set to true.

Network Interfaces

Because of a limitation of the aws_instance resource, the network interfaces don't get tags. The workaround is to use the aws_network_interface resource to create the network interfaces and then attach them to the instances. The network interface name is the project name concatenated with the instance name. For example: example-project-sample-node0001.

EIPs

There is a possibility to define the default behaviour of assigning EIPs to the instances by setting the assign_public_ip variable. If not provided, it will be set to true by default. But you can override it specifying the assign_public_ip attribute in the instances variable.

The EIP name is the project name concatenated with the instance name. For example: example-project-sample-node0001.

Route53

This componente expects a hosted zone under the same AWS account that you are creating the resource. The hosted zone name must match the zone domain. For example: example.com.

Additionaly, the project name is used as subdomain. For example: example-project.example.com.

A internal zone (aws_route53_record.internal) is created following the same subdomain pattern (example-project.example.com) and it is attached to the VPC.

By design, two records are created for each instance:

EBS Volumes

Each instance gets a root EBS volume when creating the instance. However, additional EBS volumes can be created and attached to the instance using the additional disks variable.

variable "instances" {
  ...
    additional_disks = optional(map(object({
      size            = number
      mount_point     = string
      volume_id       = optional(string, "")
      prevent_destroy = optional(bool, false)
    })), {})
  ...
}

The variable above is iterated in different local variables to create the EBS volumes and attach them to the instance.

  • local.additional_disks_to_create_prevent_destroy is a map of objects to create the EBS volumes with the prevent_destroy attribute set to true and volume_id is not provided.
  • local.additional_disks_to_create is a map of objects to create the EBS volumes with the prevent_destroy attribute set to false and volume_id is not provided.
  • local.additional_disks_to_attach is a map of objects to attach the EBS volumes to the instance. It attaches all volumes created by local.additional_disks_to_create_prevent_destroy and local.additional_disks_to_create and also the volumes that already exists (volume_id is provided).

NB: The prevent_destroy attribute is used to avoid destroying the EBS volume when running terraform destroy.

You must remove the resource manually:

terraform state list
terraform state show 'module.aws_base_infra.aws_ebs_volume.create["sample-master01_sdb"]'
terraform state rm 'module.aws_base_infra.aws_ebs_volume.create["sample-master01_sdb"]'
terraform destroy

Then, you can use this EBS volume later by providing the volume_id attribute that matches id from the terraform state show command above.

variable "instances" {
  ...
    additional_disks = {
      sdb = {
        size            = 8
        mount_point     = "/data"
        volume_id       = "vol-0a1b2c3d4e5f6g7h8"
      }
    }
  ...
}

EC2 instances

The resource aws_instance.this is used to create the instances. The instance name is the project name concatenated with the instance name. For example: example-project-sample-node0001.

The resource iterates the map of objects provided by the instances variable to create the instances. Each map key represents the instance name and must be unique.

{
  ami_id            = string
  instance_type     = string
  key_name          = optional(string, "")
  availability_zone = string
  disk_size         = optional(number, 8)
  additional_disks = optional(map(object({
    size            = number
    mount_point     = string
    volume_id       = optional(string, "")
    prevent_destroy = optional(bool, false)
  })), {})
  additional_security_groups   = optional(list(string), [])
  add_default_egress_sg_rules  = optional(bool, true)
  add_default_ingress_sg_rules = optional(bool, false)
  egress_sg_rules = optional(map(object({
    from_port   = number
    to_port     = number
    ip_protocol = string
    cidr_ipv4   = list(string)
    description = string
  })), {})
  ingress_sg_rules = optional(map(object({
    from_port   = number
    to_port     = number
    ip_protocol = string
    cidr_ipv4   = list(string)
    description = string
  })), {})
  tags = optional(map(string), {})
}

Instance parameters

  • ami_id accepts some pre-defined AMI names: amzn2, al2023, ubuntu2204. The pre-defined AMI will always get the latest AMI ID for the selected region.
  • key_name is the name of a pre-existent key created on the same region and AWS account that you are creating the resources. If not provided here, it uses the key_name variable.
  • availability_zone must be a letter that represents the AZ. For example: "a". It will be concatenated with the (aws_region.current) region name to create the AZ name.
  • disk_size is the root EBS volume size in GB. Default is 8.
  • additional_disks is a map of objects to describe additional disks to create/attach to the instance. The key must be a device name. See EBS Volumes for more details.
  • additional_security_groups is a list of additional security groups to attach to the instance. It must be a key from the security_groups map. See Security Groups for more details.
  • add_default_egress_sg_rules and add_default_ingress_sg_rules are used to add the default sg rules to the instance security group. Default is true for add_default_egress_sg_rules and false for add_default_ingress_sg_rules.
  • egress_sg_rules and ingress_sg_rules are used to add additional sg rules to the instance security group. See Security Groups for more details.

Cloud-init

The cloudinit_config.instance data source is used to create the cloud-init configuration. The cloud-init configuration is used to:

  • Change the SSH port.
  • Disable SELinux.
  • Change hostname.
  • Set search domain.

Examples

You can find an example here of how to use this module.

Repository

Dependencies

  • Terraform ~> 1.4.0
    • Although it may work with previous versions, it has not been tested.
    • I recommend using tfenv to manage Terraform versions.
  • pre-commit == 3.3.2
    • This repository uses pre-commit hooks to run some validations before committing new changes. You must install it in order to run the hooks.
    • You can install it using pip: pip install -r requirements.txt
    • Then, install the git hook scripts: pre-commit install
  • terraform-docs == v0.16.0
  • tflint == v0.47.0
  • AWS Credentials
    • You must have AWS credentials configured in order to run terraform plan and terraform apply.
    • You can configure it using AWS CLI

Authors

Module is maintained by Dyego Alexandre Eugenio

License

Apache 2 Licensed. See LICENSE for full details.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published