AWS with Terraform Tutorial: AWS EC2 Instances (12)

How to create AWS EC2 Instances with Terraform

Using the Terraform aws_instance resource block to configure, launch, and secure EC2 instances.

Welcome to our tutorial series where we dive into cloud infrastructure deployment using Terraform or OpenTofu on AWS. In this series, the fundamentals are shown, guiding you through the process of minimizing resource usage and simplifying the deployment complexities associated with cloud infrastructure. This tutorial series is a work in progress.

This comprehensive OpenTofu and Terraform tutorial guides you step-by-step through creating infrastructure in AWS using Terraform.

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.

Infrastructure as Code (IaC) helps maintain consistency, enables version control, enhances collaboration among teams, allows for easier replication of environments, streamlines the deployment and management of infrastructure boosting efficiency, and reducing errors in managing complex systems.

How to start building AWS infrastructure with Terraform: EC2

  1. Prerequisites

    Read previous sections of the tutorial: AWS with Terraform Tutorial

  2. AWS EC2

    AWS EC2 allows you to launch and manage virtual machines, known as instances, in the AWS cloud. Configuring an EC2 instance with Terraform requires many of the resources defined in the previous sections of the tutorial:

  3. Definition of EC2 Instances with Terraform

    Terraform resource "aws_instance" is used to define and launch EC2 Instances

  4. Run the Terraform Plan

    Plan, and apply the Terraform plan to select the AWS AMI.

  5. EC2 Cost

    EC2 instances have different prices depending on their type, region, and purchase model (on-demand, saving plan, or spot). Check Amazon EC2 Pricing.

  6. Common Questions About AWS EC2

    What are some advantages of using Terraform for provisioning EC2 resources compared to manual methods?, How does Terraform handle updates and changes to EC2 instances?, Can Terraform manage auto-scaling groups and load balancers alongside EC2 instances?, Does Terraform support multi-region deployments of EC2 instances?

  7. Next Steps

    Other tutorials for creating infrastructure in AWS using Terraform

Prerequisites

Read previous sections of the tutorial:

AWS EC2

Amazon Elastic Compute Cloud (EC2) is a web service offered by Amazon Web Services (AWS) that provides resizable and scalable computing capacity in the cloud. In simple terms, AWS EC2 allows you to launch and manage virtual machines, known as instances, in the AWS cloud.

  • AWS offers a variety of instance types optimized for different workloads
  • Instances are available in multiple processor architectures (x86 with 64-bit Intel Xeon and AMD EPYC, ARM with 64-bit AWS Graviton processors, and Apple 64-bit M1 and M2 processors).
ITWL Tutorials AWS Terraform Essentials EC2 12 1

Configuring an EC2 instance with Terraform requires many of the resources defined in the previous sections of the tutorial:

A Subnet

AWS Subnets are a subdivision of the IP Network assigned to the VPC. In the AWS with Terraform: The Essential Guide (4/21) – AWS VPC section the VPC was created.

In the section AWS with Terraform: The Essential Guide (5/21) - AWS Subnets four different subnets were defined (two public and two private), whose IDs can be referenced in Terraform using:

  • aws_subnet.ditwl-sn-za-pro-pub-00.id
  • aws_subnet.ditwl-sn-za-pro-pri-02.id
  • aws_subnet.ditwl-sn-zb-pro-pub-04.id
  • aws_subnet.ditwl-sn-zb-pro-pri-06.id

The subnets have routing tables assigned that send Internet traffic to either a NAT Gateway or an Internet Gateway, those resources were defined in previous sections of the tutorial:

Security groups controlling inbound and outbound traffic to the instance

An AWS Security Group acts as a virtual firewall for AWS instances and services controlling inbound and outbound traffic. It is a set of rules that filter traffic based on protocols, ports, and origins or destinations.

Three security groups were created in the section AWS with Terraform: The Essential Guide (9/21) – AWS Security Groups:

  • ditwl-sg-base-ec2: with common rules to be applied to all instances, allowing inbound connectivity to port TCP 22 from the Internet and also ICMP.
  • ditwl-sg-front-end: access rules for the Front End Instance allowing access from the Internet to TCP port 80.
  • ditwl-sg-back-end: access rules for the Back-End Instance, allowing access only from the Front-End instance to TCP port 8080.

Security groups in Terraform are referenced using its type, name, and its id property:

  • aws_security_group.ditwl-sg-base-ec2.id
  • aws_security_group.ditwl-sg-front-end.id
  • aws_security_group.ditwl-sg-back-end.id

A Key Pair for performing SSH Public Key Authentication to be injected by Cloud-init

An AWS Key Pair is a set of security credentials consisting of a public and private key. It securely connects to Amazon EC2 instances and authenticates access using Public key authentication for SSH.

In the section AWS with Terraform: The Essential Guide (10/21) - AWS Key Pairs a Key pair was locally created using ssh-keygen and the Terraform plan was configured to upload the public key to AWS. The public key is referenced in Terraform by its assigned name "ditwl-kp-config-user".

An Amazon Machine Image (AMI)

The AMI is used as a launching template that includes the operating system (O.S.), root block devices, applications included in the chosen O.S. distribution, and Cloud-init to handle the early initialization of the instance and injection of the assigned public key for user authentication. The AMI architecture needs to be the same as the CPU architecture for the Instance Type. We have chosen to use ARM 64.

The AMI to use has been defined in AWS with Terraform: The Essential Guide (11/21) - AWS AMIs. and can be accessed using data.aws_ami.ubuntu-23-04-arm64-minimal.id

An Instance Type

The instance type defines the hardware (CPU, Memory, Networking, and Storage) that will be used to run the instance.

AWS offers a variety of instance types optimized for different workloads, including general-purpose, compute-optimized, memory-optimized, and GPU instances.

The selection of the right instance type should be made by attending to its expected CPU/Memory/Network usage and the cost and availability of the desired operating system (from the AMI).

This tutorial uses t4g.micro and t4g.small.

Definition of EC2 Instances with Terraform

Previous sections of this AWS with Terraform guide have covered basic information about Terraform and AWS and have configured and used the AWS Terraform provider to create a VPC, four subnets, one Internet Gateway, two NAT Gateways, three Routing Tables, Security Groups, and a Private Key.

Terraform resource "aws_instance" is used to define and launch EC2 Instances. A minimal definition requires the following parameters:

  • ami
  • instance_type
  • subnet_id
  • key_name
  • vpc_security_group_ids

Additionally, some tags are added to enable classification, filtering, reporting, and integration with other tools like Ansible (see How to use Terraform, AWS, and Ansible Together).

For the tutorial two different EC2 Instances are created:

  • A front-end server running Ubuntu 23.04 ARM Minimal, located in a public subnet
  • A back-end server running Ubuntu 22.04 ARM Minimal, located in a private subnet

Add the following block to the terraform-aws-tutorial.tf created in previous sections.

# Front-end server running Ubuntu 23.04 ARM Minimal.
resource "aws_instance" "ditwl-ec-front-end-001" {
  ami                    = data.aws_ami.ubuntu-23-04-arm64-minimal.id
  instance_type          = "t4g.micro"
  subnet_id              = aws_subnet.ditwl-sn-za-pro-pub-00.id
  key_name               = "ditwl-kp-config-user"
  vpc_security_group_ids = [aws_security_group.ditwl-sg-base-ec2.id, aws_security_group.ditwl-sg-front-end.id]
  tags = {
    "Name"         = "ditwl-ec-front-end-001"
    "private_name" = "ditwl-ec-front-end-001"
    "public_name"  = "www"
    "app"          = "front-end"
    "app_ver"      = "2.3"
    "os"           = "ubuntu"
    "os_ver"       = "23.04"
    "os_arch"      = "arm64"
  }
}

# Back-end server running Ubuntu 23.04 ARM Minimal.
resource "aws_instance" "ditwl-ec-back-end-123" {
  ami                    = data.aws_ami.ubuntu-23-04-arm64-minimal.id
  instance_type          = "t4g.small"
  subnet_id              = aws_subnet.ditwl-sn-za-pro-pri-02.id
  key_name               = "ditwl-kp-config-user"
  vpc_security_group_ids = [aws_security_group.ditwl-sg-base-ec2.id, aws_security_group.ditwl-sg-back-end.id]
  tags = {
    "Name"         = "ditwl-ec-back-end-123"
    "private_name" = "ditwl-ec-back-end-123"
    "public_name"  = "server"
    "app"          = "back-end"
    "app_ver"      = "1.2"
    "os"           = "ubuntu"
    "os_ver"       = "23.04"
    "os_arch"      = "arm64"
  }
}

Run the Terraform Plan

Open a command line shell at the same location where the terraform-aws-tutorial.tf file is located, and, run the Terraform or OpenTofu plan, and apply the commands.

Plan using OpenTofu

Run tofu plan to generate and review the execution plan. Check each line and value to make sure that it corresponds to the desired change.

Terraform will refresh the state by comparing it with the Cloud resources and produce a plan for the resources that need to be created, updated, or destroyed.

In the past section of the tutorial, some resources were commented out to reduce the infrastructure cost during development. The EC2 instances have dependencies with previous resources, please uncomment all resources to proceed with the Terraform plan.

Run the Terraform plan:

$ tofu plan
data.aws_ami.ubuntu-23-04-arm64-minimal: Reading...
aws_key_pair.ditwl-kp-config-user: Refreshing state... [id=ditwl-kp-config-user]
...
aws_route_table_association.ditwl-rta-za-pro-pri-02: Refreshing state... [id=rtbassoc-0ed81ebdc48d803ca]
aws_main_route_table_association.ditwl-rta-default: Refreshing state... [id=rtbassoc-0de10556495905730]

OpenTofu used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

OpenTofu will perform the following actions:

  # aws_instance.ditwl-ec-back-end-123 will be created
  + resource "aws_instance" "ditwl-ec-back-end-123" {
      + ami                                  = "ami-01eba98c26f317c58"
      + instance_type                        = "t4g.small"
      + key_name                             = "ditwl-kp-config-user"
      + subnet_id                            = "subnet-0e89bd0e9d4bf7548"
      ...
      + tags_all                             = {
          + "Name"         = "ditwl-ec-back-end-123"
          + "app"          = "back-end"
          + "app_ver"      = "1.2"
          + "cost_center"  = "marketing-department"
          + "environment"  = "pro"
          + "os"           = "ubuntu"
          + "os_arch"      = "arm64"
          + "os_ver"       = "23.04"
          + "owner"        = "IT Wonder Lab"
          + "private_name" = "ditwl-ec-back-end-123"
          + "public_name"  = "server"
        }
      + vpc_security_group_ids               = [
          + "sg-0bd5ea3a5fd1e47dc",
          + "sg-0f6767966ef0f62d6",
        ]
    }

  # aws_instance.ditwl-ec-front-end-001 will be created
  + resource "aws_instance" "ditwl-ec-front-end-001" {
      + ami                                  = "ami-01eba98c26f317c58"
      + instance_type                        = "t4g.micro"
      + key_name                             = "ditwl-kp-config-user"
      + subnet_id                            = "subnet-09da811e23c212363"
      ...
      + tags_all                             = {
          + "Name"         = "ditwl-ec-front-end-001"
          + "app"          = "front-end"
          + "app_ver"      = "2.3"
          + "cost_center"  = "marketing-department"
          + "environment"  = "pro"
          + "os"           = "ubuntu"
          + "os_arch"      = "arm64"
          + "os_ver"       = "23.04"
          + "owner"        = "IT Wonder Lab"
          + "private_name" = "ditwl-ec-front-end-001"
          + "public_name"  = "www"
        }
      + vpc_security_group_ids               = [
          + "sg-02fc9a9af0e769b28",
          + "sg-0f6767966ef0f62d6",
        ]
    }

Plan: 2 to add, 0 to change, 0 to destroy.

Apply the changes using OpenTofu

Run tofu apply to generate and apply the execution plan. OpenTofu will generate a new plan and ask for confirmation before applying the changes. Review again the changes and answer yes or no to apply the changes in AWS.

$ tofu apply
data.aws_ami.ubuntu-23-04-arm64-minimal: Reading...
aws_key_pair.ditwl-kp-config-user: Refreshing state... [id=ditwl-kp-config-user]
aws_vpc.ditlw-vpc: Refreshing state... [id=vpc-0361cf67e9e74acf6]
data.aws_ami.ubuntu-23-04-arm64-minimal: Read complete after 2s [id=ami-01eba98c26f317c58]
aws_subnet.ditwl-sn-zb-pro-pri-06: Refreshing state... [id=subnet-0eae7e19db051d338]
...
aws_main_route_table_association.ditwl-rta-default: Refreshing state... [id=rtbassoc-0de10556495905730]

OpenTofu used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

OpenTofu will perform the following actions:

  # aws_instance.ditwl-ec-back-end-123 will be created
  + resource "aws_instance" "ditwl-ec-back-end-123" {
      + ami                                  = "ami-01eba98c26f317c58"
      ...
      + instance_type                        = "t4g.small"
      + key_name                             = "ditwl-kp-config-user"
      + subnet_id                            = "subnet-09da811e23c212363"
      + vpc_security_group_ids               = [
          + "sg-0bd5ea3a5fd1e47dc",
          + "sg-0f6767966ef0f62d6",
        ]
    }

  # aws_instance.ditwl-ec-front-end-001 will be created
  + resource "aws_instance" "ditwl-ec-front-end-001" {
      + ami                                  = "ami-01eba98c26f317c58"
      ...
      + key_name                             = "ditwl-kp-config-user"
      + subnet_id                            = "subnet-09da811e23c212363"
      ...
      + vpc_security_group_ids               = [
          + "sg-02fc9a9af0e769b28",
          + "sg-0f6767966ef0f62d6",
        ]
    }

Plan: 2 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  OpenTofu will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_instance.ditwl-ec-back-end-123: Creating...
aws_instance.ditwl-ec-front-end-001: Creating...
aws_instance.ditwl-ec-back-end-123: Still creating... [10s elapsed]
aws_instance.ditwl-ec-front-end-001: Still creating... [10s elapsed]
aws_instance.ditwl-ec-back-end-123: Creation complete after 16s [id=i-0c4927b77237710fb]
aws_instance.ditwl-ec-front-end-001: Creation complete after 16s [id=i-0286e96ae119c8391]
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Review the result, it should be 2 (resource) added, 0 changed, 0 destroyed.

You can check that the EC2 instances have been created in the AWS Console.

Navigate to AWS Console: EC2 - Instances.

Instances EC2 us east 1

Destroy the infrastructure using OpenTofu

The time that the infrastructure is running will be billed by AWS. To reduce the cost during Terraform development or when the infrastructure is not needed anymore terraform destroy will be used.

Terraform destroy terminates all the resources created by the plan.

Run tofu destroy to generate and apply the destruction plan. OpenTofu will generate a new plan with the resources to terminate and ask for confirmation before applying the changes.

All data will be lost but you will be able to recreate the infrastructure when needed using tofu apply.

$ tofu destroy
data.aws_ami.ubuntu-23-04-arm64-minimal: Reading...
aws_key_pair.ditwl-kp-config-user: Refreshing state... [id=ditwl-kp-config-user]
....
aws_main_route_table_association.ditwl-rta-default: Refreshing state... [id=rtbassoc-0de10556495905730]

OpenTofu used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  - destroy

OpenTofu will perform the following actions:

  # aws_instance.ditwl-ec-back-end-123 will be destroyed
  - resource "aws_instance" "ditwl-ec-back-end-123" {
      - ami                                  = "ami-01eba98c26f317c58" -> null
      - id                                   = "i-0d337cae95cba251d" -> null
    }
  ...
  # aws_route_table_association.ditwl-rta-zb-pro-pri-06 will be destroyed
  - resource "aws_route_table_association" "ditwl-rta-zb-pro-pri-06" {
      - id             = "rtbassoc-0de046c1296c9483a" -> null
      - route_table_id = "rtb-0f9a668ee7cc5fb83" -> null
      - subnet_id      = "subnet-0eae7e19db051d338" -> null
    }
  ...
  # aws_subnet.ditwl-sn-zb-pro-pub-04 will be destroyed
  - resource "aws_subnet" "ditwl-sn-zb-pro-pub-04" {
   ...
    }

  # aws_vpc.ditlw-vpc will be destroyed
  - resource "aws_vpc" "ditlw-vpc" {
   ...
    }

Plan: 0 to add, 0 to change, 23 to destroy.

Do you really want to destroy all resources?
  OpenTofu will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

aws_route_table_association.ditwl-rta-zb-pro-pri-06: Destroying... [id=rtbassoc-0de046c1296c9483a]
aws_subnet.ditwl-sn-zb-pro-pub-04: Destroying... [id=subnet-03d11ad36cc9e13d8]
...
aws_subnet.ditwl-sn-za-pro-pub-00: Destroying... [id=subnet-09da811e23c212363]
aws_security_group.ditwl-sg-front-end: Destroying... [id=sg-02fc9a9af0e769b28]
aws_security_group.ditwl-sg-base-ec2: Destroying... [id=sg-0f6767966ef0f62d6]
aws_subnet.ditwl-sn-za-pro-pri-02: Destruction complete after 1s
aws_subnet.ditwl-sn-za-pro-pub-00: Destruction complete after 1s
aws_security_group.ditwl-sg-back-end: Destruction complete after 2s
aws_security_group.ditwl-sg-front-end: Destruction complete after 2s
aws_security_group.ditwl-sg-base-ec2: Destruction complete after 2s
aws_vpc.ditlw-vpc: Destroying... [id=vpc-0361cf67e9e74acf6]
aws_vpc.ditlw-vpc: Destruction complete after 4s

Destroy complete! Resources: 29 destroyed.

AWS EC2 Cost

EC2 instances have different prices depending on their type, region, and purchase model (on-demand, saving plan, or spot). Check Amazon EC2 Pricing.

Common Questions About AWS EC2

What are some advantages of using Terraform for provisioning EC2 resources compared to manual methods?

  • Infrastructure as Code (IaC): Terraform allows you to define your infrastructure as code, which means you can version control, review, and collaborate on infrastructure changes just like you do with application code. This ensures consistency and repeatability in your infrastructure deployments.
  • Automation: Terraform automates the provisioning process, reducing the likelihood of human errors that can occur with manual configurations. This leads to more reliable and consistent infrastructure deployments.
  • Ease of Management: With Terraform, you can manage your entire infrastructure stack, including EC2 instances, security groups, IAM roles, and other resources, in a single configuration file. This simplifies management and reduces the need for manual intervention.
  • Resource Tracking and Dependency Management: Terraform automatically tracks dependencies between resources, ensuring that they are created, updated, or destroyed in the correct order. This helps prevent issues such as resource conflicts or missing dependencies.
  • Scalability: Terraform makes it easy to scale your infrastructure up or down based on demand by simply adjusting the configuration file. You can quickly add or remove EC2 instances, update instance types, or modify other parameters as needed.
  • Integration with Other Tools: Terraform integrates seamlessly with other DevOps tools and workflows, such as version control systems (e.g., Git), continuous integration/continuous deployment (CI/CD) pipelines, and configuration management tools. This allows you to incorporate infrastructure changes into your existing workflows without disruption.

How does Terraform handle updates and changes to EC2 instances?

Changes in Terraform EC2 instance definition (e.g., modifying instance type, adding tags, updating security group rules) are detected by Terraform during the planning phase.

The current state of the infrastructure (as tracked in the Terraform state file) is compared with the desired state defined in the configuration and a plan is generated.

In some cases, changes may require replacing existing EC2 instances rather than updating them in place. This typically happens when changes are not compatible with in-place updates (e.g., changing instance size or AMI). Terraform handles replacement by first creating a new instance with the updated configuration, and then terminating the old instance once the new one is ready.

Terraform ensures that changes to EC2 instances are applied in the correct order, taking into account dependencies between resources.

Can Terraform manage auto-scaling groups and load balancers alongside EC2 instances?

Yes, Terraform can manage auto-scaling groups (resource "aws_autoscaling_group") and load balancers (resource "aws_lb") alongside EC2 instances.

Does Terraform support multi-region deployments of EC2 instances?

Yes, by using a different AWS provider (with an alias) for each region.

Next Steps

This tutorial series is a work in progress and will have these sections:

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

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 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.
How to configure and use the Terraform aws_key_pair resource block to create and manage AWS Key Pairs for performing SSH Public Key Authentication into 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