Terraform is an open-source Infrastructure as Code (IaC) tool created by HashiCorp. With Terraform, you define your infrastructure using a declarative configuration language called HashiCorp Configuration Language (HCL) (or optionally JSON), then Terraform plans and applies the changes via provider APIs.
In this guide, we’ll use Terraform to provision a basic EC2 instance in AWS and output the instance IP address so you can SSH into it.
Also check:
- Terraform AWS VPC with Public and Private subnets with NAT
- Using Terraform to Launch a VPS Instance in Digital Ocean
- Create an RDS instance in Terraform with a MariaDB Example
- Using Terraform to launch a DigitalOcean Kubernetes Cluster (Part 1)
- How to Create an AWS Lightsail Instance With Terraform
Prerequisites
To follow along this guide, you need the following:
- An AWS account and IAM credentials with permissions to manage EC2 (and related networking resources like security groups).
- (Optional but recommended) AWS CLI installed. Install it here.
- Terraform installed. Get it from here.
- A VPC ID and a subnet ID where you want the instance to run (ideally a public subnet if you want a public IP).
- An SSH key pair (a local private key + public key).
This is the Terraform version I am using:
| |
Configure AWS credentials
Terraform uses the same credential discovery chain as the AWS SDK. You can use any of these approaches:
- AWS CLI profile (recommended): run
aws configurethen exportAWS_PROFILE. - Environment variables: export access key + secret key.
- IAM role: if you’re running on an AWS environment (e.g. CloudShell/EC2), attach a role.
Here is the environment variable approach:
| |
Project files
Create a new directory for this demo (for example terraform-ec2/) and add these files:
versions.tffor Terraform + provider requirementsvariables.tffor inputsmain.tffor resourcesoutputs.tffor outputs
versions.tf (Terraform + provider requirements)
Pinning provider versions makes runs more predictable across machines and over time.
| |
variables.tf (inputs)
We’ll keep this example configurable (region, VPC, subnet, instance type, SSH CIDR, etc). Note that the SSH ingress CIDR is intentionally required so you don’t accidentally open port 22 to the whole internet.
| |
main.tf (provider + resources)
Adding a provider
A Terraform provider is an integration that understands how to talk to an API (AWS in our case). We’ll also configure the region via a variable.
| |
Defining the key pair
This creates an EC2 key pair in AWS from your local public key. Note that Terraform identifiers (like aws_key_pair.ssh_key) use letters, digits, and underscores (no hyphens).
| |
Defining the security group
A security group acts as a virtual firewall for your EC2 instances. We’ll allow SSH (port 22) only from a single CIDR you specify, and allow all outbound traffic.
| |
Creating the EC2 instance
Now we can create an EC2 instance. We’ll attach the security group, place it in your chosen subnet, and associate a public IP (useful for quick SSH access in a public subnet).
| |
outputs.tf (useful outputs)
Terraform outputs allow you to export values from your state so you can use them elsewhere. We’ll output the public/private IP plus a ready-to-copy SSH command.
| |
Note: the default user depends on the AMI you choose. For example:
- Amazon Linux:
ec2-user - Ubuntu:
ubuntu - Debian:
adminordebian(varies by image)
terraform.tfvars (example values)
Create a terraform.tfvars file and populate your own values:
| |
Tip: you can get your public IP and then append /32 (for example 203.0.113.10/32) to restrict SSH to only your current IP.
Creating resources
With the files in place, let’s create the AWS resources:
| |
After apply completes, Terraform will print outputs (including the public IP). You can also view outputs any time with:
| |
Conclusion
In this guide, we provisioned an EC2 instance with Terraform, restricted SSH access via a security group, and exported useful outputs (including the public IP). From here you can extend the configuration by adding a proper VPC, IAM roles, EBS encryption, user-data bootstrapping, and tighter network rules.