Tutorial and source code explaining how to manage AWS EC2 Instances and Security with Terraform.

EC2 Instances and Resource Security

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

Continue the demo, see:

Download de the code from IT Wonder Lab public GitHub repository.

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"
  }

Following IT Wonder Lab best practices the EC2 elements will be named following a pattern:

  • Cloud: a prefix specifying the unique name of this cloud across all available clouds and providers. In this case the prefix will be: ditwl that stands for Demo ITWonder Lab in lowercase.
  • Resource: a short name identifying the resource, in this case:
    • ec2for EC2 Instances
    • sg-ec2for an EC2 Security Group 
    • sr-ec2for an EC2 Security Rule 
  • Environment: for resources that are not to be shared between environments, a 3 letter acronym for the environment:
    • pro: production
    • pre: preproduction
    • dev: development
  • Visibility: for resources that can be either public or private, a 3 letter acronym for the visibility:
    • pub: for public resources
    • pri: for private resources
  • Name/ID: optional a name or ID that describes the usage of the resource or the number of the resource instance, for example:
    • EC2 resource number: this will be an EC2 instance for WordPress wp number 01 for Public Zones and Pro Environment. We might have new EC2 instances for WP in the future, adding a number now will make it easier to grow the infrastructure later on.
    • EC2 security group number: this will be the EC2 security group 01 for Public Zones and Pro Environment for wp. We might have new security groups on the future, adding a number now will make it easier to grow the infrastructure later on.
    • Description of the purpose of the rule: using a description like instances_to_instance_port explains the intended usage of the rule. In this case the rules allow access from the Internet to ports 80 and 443. (as we are not using a Load Balancer, Internet connections are directly to instances)

Private Key for the EC2 Instance

In order 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 AWS EC2 console, it can be uploaded to the console or created using a wizard.

Visit the EC2 AWS web console and go to Network & Security and Key Pairs. Create a new Key Pair and name it ditwl_kp_infradmin. AWS generates a PEM file that you should store in a safe place.

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 destroy. 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_sec_group_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 to a previously created security group, the cidr_blocks module uses an IP address range as source for the traffic.
  • modules/aws/security/rule/source_group: creates a security rule associated to a previously created security group, thesource_group module uses another security group as source. See RDS creation for an example of its usage.

Following IT Wonder Lab best practices for AWS security groups, the following groups are created for each resource.

  • A specific group for each resource:  It is recommended to have a group for each resource, it will be named using part of the resource name and the prefix will have the cloud and the resource type sg. Examples:
    • ditwl-aws-sg-rds-mariadb-pro-pub-01: security group for all the MariaDB RDS resources.
    • ditwl-sg-ec2-pro-pub-01: security group for all the WP EC2 instances.
  • A generic default group for each resource type: these groups are used to hold default groups that apply to the type of resource, for example the SSH access to EC2 instances from a fixed administration IP address or the access to the database port for administration from a fixed administration IP. Examples:
    • ditwl-sg-rds-mariadb-def: default security group for all the MariaDB RDS resources. It is specific for MariaDB RDS resources as other type of database will use a different port.
    • ditwl-sg-ec2-def: default security group for all EC2 instances. It will have the SSH access rule.

Avoid creating too many groups and don’t use CIDR as a source (except for Internet as a source). It is better to use groups as a source, that way an element gets access to other resources by being a member of a group, not by having a specific IP that can change.

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

List of security rules

AWSC Security rules applied to an EC2 and RDS instances

Continue the demo, see:

How useful was this post?

Click on a star to rate it!

Average rating / 5. Vote count:

We are sorry that this post was not useful for you!

Let us improve this post!

Categories: TutorialTerraformAWS

Leave a Reply

avatar

This site uses Akismet to reduce spam. Learn how your comment data is processed.