Creating AWS EC2 Instances and Security Rules with Terraform (5/5)

EC2 Instances and Resource Security

This is the continuation of an AWS Terraform demo to create a VPC in AWS with an EC2 instance connected to the MariaDB database running in RDS using a single Terraform plan.

Click to see the New updated tutorial on AWS with Terraform and OpenTufu

AWS and Terraform Tutorial: The Essential Guide for Beginners

Terraform Basics, AWS Basics, Terraform AWS Provider, AWS VPC, AWS Subnets, AWS Internet Gateway, AWS NAT Gateway, AWS Routing Tables, AWS Security Groups, AWS Key Pairs, AWS AMIs, AWS EC2 Instances, AWS RDS, AWS Route 53 (DNS), AWS Auto Scaling, AWS Load Balancers, Terraform AWS & Ansible, Terraform Modules, Terraform Backends, Terraform Tools, Terraform CI/CD.

AWS with Terraform: The Essential Guide: Sections

Our new tutorial series delves into cloud infrastructure deployment using Terraform or OpenTofu on AWS. In this series, the fundamentals are shown, guiding you through minimizing resource usage and simplifying the deployment complexities associated with cloud infrastructure. This tutorial series is a work in progress and will have these sections:

Prerequisites and source code

Creating AWS EC2 Instances with Terraform

EC2 instances are defined using the terraform.tfvars, some values (ami, vpc_security_group_ids, and subnet_id) are derived from modules output so the definition is in the aws_ec2_pro_wp.tf file as terraform.tfvars doesn't allow interpolation.

        #------------------------
    # WP PRO
    #------------------------

  aws_ec2_pro_pub_wp_01 = {
    name              = "ditwl-ec2-pro-pub-wp01"
    ami               = "" #Uses data.aws_ami.ubuntu1604.id
    instance_type     = "t2.micro" #AWS Free Tier: 750 hours per month of Linux, RHEL, or SLES t2.micro instance usage
    availability_zone = "us-east-1a"
    key_name          = "ditwl_kp_infradmin"
    # vpc_security_group_ids = SEE TF file
    # subnet_id         = SEE TF file
    associate_public_ip_address = true

    root_block_device_size        = 8

    # See https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSVolumeTypes.html
    root_block_device_volume_type = "gp2"

    tag_private_name  = "ditwl-ec2-pro-pub-wp-01"
    tag_public_name   = "www"
    tag_app           = "wp"
    tag_app_id        = "wp-01"
    tag_os            = "ubuntu"
    tag_os_id         = "ubuntu-16"
    tags_environment  = "pro"
    tag_cost_center   = "ditwl-permanent"
    tags_volume       = "ditwl-ec2-pro-pub-wp-01-root"

  }

  #------------------------
  # WP PRO Security Group
  #------------------------
  aws_sg_ec2_pro_pub_wp_01 = {
    sec_name        = "ditwl-sg-ec2-pro-pub-01"
    sec_description = "ditwl - WP server access rules - Pub, Env: PRO"
    allow_all_outbound = false
  }

  aws_sr_ec2_pro_pub_wp_01_internet_to_80 = {
    type              = "ingress"
    from_port         = 80
    to_port           = 80
    protocol          = "tcp"
    cidr_blocks       = "0.0.0.0/0"
    description       = "Access from Internet to port 80"
  }

  aws_sr_ec2_pro_pub_wp_01_internet_to_443 = {
    type              = "ingress"
    from_port         = 443
    to_port           = 443
    protocol          = "tcp"
    cidr_blocks       = "0.0.0.0/0"
    description       = "Access from Internet to port 443"
  }

All EC2 instance names and their Security Rules and Groups follow a standardized naming pattern.

Private Key for the EC2 Instance

To access the created Linux instances in AWS you will need an SSH client. Authentication will use a private key and in the case of Ubuntu a username named "ubuntu".

The private key needs to be registered in the AWS EC2 console, it can be uploaded to the console or created using a wizard.

  1. Visit the AWS console
  2. Select the region where instances will be created (as Key Pais are unique to each region),
  3. Go to the EC2 AWS web console
  4. Go to Network & Security and Key Pairs.
  5. Create a new Key Pair and name it ditwl_kp_infradmin. AWS generates a PEM file that you should store in a safe place.
  6. Save the downloaded pem file in ${HOME}/keys/ditwl_kp_infradmin.pem. It will be used by Ansible in the next tutorial.

Launching AWS EC2 Instances with Terraform

Having configured most of the values for the instance in the terraform.tfvars, now the file aws_ec2_pro_wp.tf makes use of Terraform modules to create the resources.

The Terraform module /modules/aws/ec2/instance/add is used to create the EC2 instance. Most of the variables come from the aws_ec2_pro_pub_wp_01 variable definition from terraform.tfvars and the rest are interpolations to other resources:

  • ami = "${data.aws_ami.ubuntu1604.id}". Uses the output from the data source aws_ami. See Route 53 and AMI Lookup for an explanation.
  • disable_api_termination = "${var.is_production ? true : false}". Uses the value from is_production variable to prevent EC2 instance destruction. See Avoiding AWS instance destroy with Terraform for an explanation.
  • vpc_security_group_ids = ["${module.aws_sec_group_ec2_default.id}", "${module.aws_sg_ec2_pro_pub_wp_01.id}"] is a list of security groups that will allow access to the instance.
  • register_dns_private = true and register_dns_public = true are used for Instance registration in private and public DNS. See Registering EC2 instances in Route 53 for an explanation.
  # Create WP instance
  module "aws_ec2_pro_pub_wp_01" {
    source            = "./modules/aws/ec2/instance/add"
    name              = var.aws_ec2_pro_pub_wp_01["name"]
    ami               = data.aws_ami.ubuntu1604.id
    instance_type     = var.aws_ec2_pro_pub_wp_01["instance_type"]
    availability_zone = var.aws_ec2_pro_pub_wp_01["availability_zone"]
    key_name          = var.aws_ec2_pro_pub_wp_01["key_name"]
    disable_api_termination = var.is_production ? true : false
    vpc_security_group_ids = [module.aws_sg_ec2_default.id,module.aws_sg_ec2_pro_pub_wp_01.id]
    subnet_id         = module.aws_sn_za_pro_pub_32.id
    associate_public_ip_address = var.aws_ec2_pro_pub_wp_01["associate_public_ip_address"]
    instance_tags     = {}
    tag_private_name  = var.aws_ec2_pro_pub_wp_01["tag_private_name"]
    tag_public_name   = var.aws_ec2_pro_pub_wp_01["tag_public_name"]
    tag_app           = var.aws_ec2_pro_pub_wp_01["tag_app"]
    tag_app_id        = var.aws_ec2_pro_pub_wp_01["tag_app_id"]
    tag_os            = var.aws_ec2_pro_pub_wp_01["tag_os"]
    tag_os_id         = var.aws_ec2_pro_pub_wp_01["tag_os_id"]
    tags_environment  = var.aws_ec2_pro_pub_wp_01["tags_environment"]
    tag_cost_center   = var.aws_ec2_pro_pub_wp_01["tag_cost_center"]

    register_dns_private = true
    route53_private_zone_id = module.aws_route53_public.id

    register_dns_public = true
    route53_public_zone_id = module.aws_route53_public.id

    root_block_device = {
      volume_size           = var.aws_ec2_pro_pub_wp_01["root_block_device_size"]
      volume_type           = var.aws_ec2_pro_pub_wp_01["root_block_device_volume_type"]
      delete_on_termination = var.is_production ? false : true #If production, Do not delete!
    }

    volume_tags       = {
      Name = var.aws_ec2_pro_pub_wp_01["name"]
    }

    ignore_changes = ["ami"]

  }

Resource Security in AWS with Terraform

Securing AWS VPC resources with Terraform makes use of 3 modules:

  • modules/aws/security/group: creates a security group and returns the ID to be used in rules and instance association.
  • modules/aws/security/rule/cidr_blocks: creates a security rule associated with a previously created security group, the cidr_blocks module uses an IP address range as the source for the traffic.
  • modules/aws/security/rule/source_group: creates a security rule associated with a previously created security group, the source_group module uses another security group as source. See RDS creation for an example of its usage.

See AWS Security Groups' Best Practices.

The following illustration shows the security groups and rules applied to each AWS resource.

List of security rules
AWSC Security rules applied to EC2 and RDS instances

Continue the Terraform and Ansible demo, see:

Next Steps

Leave a Reply

Your email address will not be published. Required fields are marked *


Related Cloud Tutorials

Securing your Infrastructure: Encrypting Terraform State Files with OpenTofu
Using the Terraform aws_route53_delegation_set, aws_route53_zone, and aws_route53_record resource blocks to configure DNS in AWS.
Using the Terraform aws_db_instance resource block to configure, launch, and secure RDS instances.
How to use the Terraform aws_instance resource block to configure, launch, and secure EC2 instances.
How to configure and use the Terraform aws_ami data source block to find and use AWS AMIs as templates (root volume snapshot with operating system and applications) for EC2 instances.
Javier Ruiz Cloud and SaaS Expert

Javier Ruiz

IT Wonder Lab tutorials are based on the diverse experience of Javier Ruiz, who founded and bootstrapped a SaaS company in the energy sector. His company, later acquired by a NASDAQ traded company, managed over €2 billion per year of electricity for prominent energy producers across Europe and America. Javier has over 25 years of experience in building and managing IT companies, developing cloud infrastructure, leading cross-functional teams, and transitioning his own company from on-premises, consulting, and custom software development to a successful SaaS model that scaled globally.

Are you looking for cloud automation best practices tailored to your company?

linkedin facebook pinterest youtube rss twitter instagram facebook-blank rss-blank linkedin-blank pinterest youtube twitter instagram