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.
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
Read previous sections of the tutorial: AWS with Terraform Tutorial
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:
Definition of EC2 Instances with Terraform
Terraform resource "aws_instance" is used to define and launch EC2 Instances
Plan, and apply the Terraform plan to select the AWS AMI.
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?, 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?
Other tutorials for creating infrastructure in AWS using Terraform
Read previous sections of the tutorial:
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.
Configuring an EC2 instance with Terraform requires many of the resources defined in the previous sections of the tutorial:
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:
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:
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:
Security groups in Terraform are referenced using its type, name, and its id property:
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"
.
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
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.
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:
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:
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" } }
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.
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.
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.
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.
EC2 instances have different prices depending on their type, region, and purchase model (on-demand, saving plan, or spot). Check Amazon EC2 Pricing.
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.
Yes, Terraform can manage auto-scaling groups (resource "aws_autoscaling_group"
) and load balancers (resource "aws_lb"
) alongside EC2 instances.
Yes, by using a different AWS provider (with an alias) for each region.
This tutorial series is a work in progress and will have these sections:
AWS with Terraform: The Essential Guide: Sections
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?