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.
Prerequisites and source code #
- Terraform Setup and AWS VPC Subnet Creation (1/5)
- VPC Subnet Routing and Internet Access with Terraform (2/5)
- AWS Route 53, AMI Lookup and EC2 Creation with Terraform (3/5)
- Creating AWS RDS Database with Terraform (4/5)
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.
- Visit the AWS console
- Select the region where instances will be created (as key pairs are unique to each region),
- Go to the EC2 AWS web console
- 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 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.

Next Steps #
- AWS Terraform Essential Guide, a comprehensive tutorial that guides you step-by-step through creating infrastructure in AWS using Terraform
- Terraform AWS Tutorials
- Ansible Tutorials