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

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

- Source: https://www.itwonderlab.com/terraform-aws-ec2-security-rules/
- Published: 2020-06-26
- Updated: 2026-10-05
- Author: Javier Ruiz Jiménez (https://www.javierruizjimenez.com/)
- Site: IT Wonder Lab (https://www.itwonderlab.com/)

---

## 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.

> [!NOTE]
> This is one of the original IT Wonder Lab tutorials, written for Terraform 0.12 (2018-2020) with the AWS provider of that time. The ideas and the structure are still valid, but some arguments and versions have changed. For a current, step-by-step version see [AWS with Terraform: The Essential Guide](https://www.itwonderlab.com/aws-terraform-tutorial-terraform-basics/), and use OpenTofu or a recent Terraform with the commands `tofu` or `terraform`.

### Prerequisites and source code

-   [Terraform Setup and AWS VPC Subnet Creation (1/5)](https://www.itwonderlab.com/terraform-ansible-aws-howto/)
-   [VPC Subnet Routing and Internet Access with Terraform (2/5)](https://www.itwonderlab.com/using-terraform-to-create-an-aws-vpc-with-an-ec2-instance-and-a-mariadb-rds-data-base-ii/)
-   [AWS Route 53, AMI Lookup and EC2 Creation with Terraform (3/5)](https://www.itwonderlab.com/terraform-route-53-ami-lookup/)
-   [Creating AWS RDS Database with Terraform (4/5)](https://www.itwonderlab.com/terraform-aws-mariadb-rds-database/)

### 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.

```json title="terraform.tfvars"
        #------------------------
    # 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](https://www.itwonderlab.com/aws-and-terraform-naming-best-practices/).

### 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 pairs 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](https://www.itwonderlab.com/terraform-route-53-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](https://www.itwonderlab.com/avoiding-instance-destroy-aws-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](https://www.itwonderlab.com/terraform-route-53-ami-lookup/) for an explanation.

```json title="aws_ec2_pro_wp.tf"
  # 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](https://www.itwonderlab.com/aws-security-groups-best-practices/).

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

![List of security rules](https://www.itwonderlab.com/media/Terraform/terraform-aws-ec2-rds-basic-free/ITWL_AWS_Terraform_VPC_WP_Security1.png "AWSC Security rules applied to EC2 and RDS instances")

### Next Steps

-   [AWS Terraform Essential Guide, a comprehensive tutorial that guides you step-by-step through creating infrastructure in AWS using Terraform](https://www.itwonderlab.com/tag/aws-terraform-essential-guide/)
-   Terraform AWS Tutorials
    -   [Terraform Setup and AWS VPC Subnet Creation (1/5)](https://www.itwonderlab.com/terraform-ansible-aws-howto/)
    -   [VPC Subnet Routing and Internet Access with Terraform (2/5)](https://www.itwonderlab.com/using-terraform-to-create-an-aws-vpc-with-an-ec2-instance-and-a-mariadb-rds-data-base-ii/)
    -   [AWS Route 53, AMI Lookup and EC2 Creation with Terraform (3/5)](https://www.itwonderlab.com/terraform-route-53-ami-lookup/)
    -   [AWS RDS MariaDB Database and Resource Tags with Terraform (4/5)](https://www.itwonderlab.com/terraform-aws-mariadb-rds-database/)
    -   [EC2 Instances and Resource Security with Terraform (5/5)](https://www.itwonderlab.com/terraform-aws-ec2-security-rules/)
-   Ansible Tutorials
    -   [Using Ansible for Terraform Managed AWS Infrastructure](https://www.itwonderlab.com/terraform-aws-ansible/)
