In this guide we will learn the most important concepts of terraform and write terraform code to create an EC2 instance in AWS using our specified AMI ID using concepts like providers, backend, variables, resources, data and outputs.
Page Contents
Introduction
Terraform is an open-source infrastructure as code (IaC) tool that is used to automate the creation, modification, and versioning of infrastructure resources in the cloud. It uses a declarative syntax to define the desired state of the infrastructure, and then applies changes to make the actual state match the desired state.
Terraform simplifies and streamlines the process of managing infrastructure and allows teams to codify their infrastructure as code, which can be version-controlled, tested, and shared like any other code which leads to better collaboration, faster feedback loops, and more reliable infrastructure.
Core Concepts
If you are familiar with any programming language then it will be very easy to understand and use terraform. If you want to learn about terraform superfast then lets forget about all complex stuffs of terraform and start with simple but core stuffs. Terrraform consists of coding concepts like:
- Variables: It is a key-value pair used by Terraform to allow customization.
- Provider: It is a plugin to interact with APIs of service and access its related resources.
- State: It consists of cached information about the infrastructure managed by Terraform and its related configurations. Its extension is .tfstate
- Resources: It refers to a block of one or more infrastructure objects (compute instances, virtual networks, etc.), which are used in configuring and managing the infrastructure.
- Data Source: It is implemented by providers to return information on external objects to terraform.
- Output Values: These are return values of a terraform module that can be used by other configurations.
- Module: It is a folder with Terraform templates where all the configurations are defined
- .tf files: It is our terraform file where we write terraform code. All of these .tf files are executed while running terraform.
Also the main commands of terraform are:
- terraform init: It is used to initialized our terraform project. It deals with downloading required plugins and configuring state files.
- terraform plan: It is used to see what resources will be created by our terraform code.
- terraform apply: It basically creates resources defined in the code.
- terraform destory: It basically destroys every resources that are created by terraform. How does it know what resources are created by terraform? It read .tfstate file for this information.
Enough theory, Now lets create a basic yet enlightening project to create an EC2 instance in AWS and learn about all the fundamental stuffs in action
Getting started
Prerequisites:
- Terraform installed in your device – Installation Link
- An AWS account obviously if you want to test it out
- An IAM user with programmatic access for terraform. You can give administrator privilege or specific privileges as required
- A S3 bucket with access to the IAM user
- AWS CLI with Access keys configured in your custom profile
Now, let’s start with defining which provider we will need for our terraform project and where to store our .tfstate file.
We will create a main.tf file and insert the following code into it:
terraform {
## "required_providers" is used to define which providers we need ##
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.18.0"
}
}
## "backend" section is used to define where we want to store our tfstate file ## Profile helps to define which AWS profile to use ##
backend "s3" {
bucket = "milan-terraform"
key = "demo1/demo1-terraform.tfstate"
region = "ap-south-1"
profile = "milan-aws"
}
}
## This code block defines our provider. We are using AWS in this case. ##
provider "aws" {
region = "ap-south-1"
profile = "milan-aws"
}
The above terraform block is our main block where:
Plugins
“required_providers” is used to define which providers we need, i.e. AWS in our case. Basically we are defining a plugin that is required.
Backend
“backend” section is used to define where we want to store our tfstate file. In our case we will be using the S3 bucket that we created earlier. We can also store this file locally and we can just remove this part to do it.
Also in “key” section its value is given like this: “path/another_path/terraform.tfstate”. This path will be created automatically when initializing terraform.
Profile helps to define which AWS profile to use. We will be using the profile that we have configured.
Providers
In provider Section we have defined provider i.e. “aws”. It is the actual section where we specify details of our provider.
Getting AMI ID
If we want to create an EC2 instance using terraform then we will need an AMI ID. Normally we select AMI from AWS UI. Now we need to do the same using terraform.
Our main goal here is to find ID of AMI which is based on ubuntu and its name must start with “ubuntu-focal-20.04-amd64-server-*” with a wildcard.
Create a new file called ami.tf and insert the following code:
data "aws_ami" "ubuntu" {
most_recent = true
## Filters to get our desired AMI ##
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
## Owners refer to AWS Account ID and this belongs to Canonical ##
owners = ["099720109477"]
}
output "aws_ami" {
value = data.aws_ami.ubuntu.id
}
Data
This “data” block is used by terraform to collect data. It uses provider “aws” where terraform will look into “aws_ami” for “ubuntu” OS in AWS AMI repository. It will look specifically into AWS account ID 099720109477 using filters as given in the code above.
Output
This “Output Block” is like a console output. It will output aws_ami with value that is pulled from data.aws_ami.ubuntu i.e. from above “data” block. “id” is used here to retrieve id only. If not used then it will give many other details that we dont need
Now lets test whether this works or not. Make sure you are in your terraform directory which consists main.tf and ami.tf and follow these commands:
This first command is done to initialize our terraform. Terraform will download necessary plugins and configure S3 bucket in our case. It is a mandatory command.
terraform init

Now our second command is for planning i.e. for seeing what resources will be created by terraform. In our case, no resources will be created. Our terraform code is supposed to only scan AMIs and give value of our AMI ID. Also, we can already see our AMI ID in planning state. If we had specified resources in our code then it wouldn’t have created resource, it would only display what resource to create in planning state.
terraform plan

Now lets enter our final command. This command actually creates resources if we have specified in our code.
terraform apply

Our code has worked successfully. And we can see Outputs with our ami-id.
Also lets check whether our .tfstate file is in S3 bucket or not.

Now we will use this ami id to create a EC2 instance.
Creating an EC2 Instance
Before creating EC2 instance, make sure you already have a key-pair in your AWS account.
We can also create key-pair using terraform but we wont be doing that here.
Also, we will be using default VPC, subnet ID and security group while creating our EC2 instance. So we don’t have to worry about them as well.
Local Variables
Lets start with defining some local variables. Local variables are variables where we can assign some vaule and use them.
Create a file called locals.tf:
locals {
instance1-type = "t2.nano"
key-name = "milan"
}
In the above code block “locals” there are two variables in it. One is instance type and another is the keypair we will be using for creating EC2 instance.
Create a file called EC2.tf:
resource "aws_instance" "demo1" {
ami = data.aws_ami.ubuntu.id
instance_type = local.instance1-type
key_name = local.key-name
tags = {
Name = "Terraform-EC2 Milan"
}
}
output "Instance-Public-IP" {
value = aws_instance.demo1.public_ip
}
Resources
In the above code there is a block called “resources” that consists “aws_instance” and it also consists of keyword “demo1”. This code block is basically telling terraform to create a resource of type aws instance. Here, “demo1” is just a reference name for this resource. We can set it to anything. All “resource” blocks must have a reference name. If we want to create other resource like eg security group or VPC then we will define resource similarly like this one.
- ami: It defines which ami ID to use and we are using ami id that we got from ami.tf
- instance_type: It defines which instance type to use. We can directly specify instance type we want but in our case we are taking value of our variable that we defined in locals section. We can call local variable from any code block in any .tf file by using local.variable-name.
- key_name: It defines which key to use for the EC2 instance.
- Output: We want to get public IP of our instance so we are using “aws_instance.demo1.public_ip” to get public IP.
Now lets test this in action:
terraform plan


The output is self explainatory.
Now lets apply it:
terraform apply
Enter yes


Our instance has been created successfully and we can also see the Public IP as defined in out output section.
Check your AWS acount and you should see EC2 instance just like you defined in your terraform code.
Thats it!! You have successfully created an EC2 instance using terraform and learnt the basics of terraform. This should give you enough knowledge and confidence to starting working on complex terraform projects.