Deploy an EC2 Instance with Terraform
Use Terraform to provision and manage an EC2 instance on AWS.
Introduction
30 Second Summary
Every time a company needs a new server, someone has to set it up. Clicking through a cloud dashboard works for one server, but what happens when you need ten, or a hundred, all configured the same way?
In this project, you will write Terraform configuration from scratch to deploy a virtual server on AWS. You will launch it with a single command and tear it down just as easily.
What You'll Build
You will create a Terraform project that provisions an EC2 instance (a virtual machine) on AWS using Infrastructure as Code.
By the end of this project, you'll have:
- A terraform.tf file defining your Terraform provider configuration.
- A main.tf file defining an EC2 instance resource with a dynamic AMI data source.
- Hands-on experience with the core Terraform workflow: init, plan, apply, and destroy.
- Secret Mission: Add an output value and a security group to make your instance publicly accessible on port 80.
Are there any prerequisites?
None. This project includes full setup instructions for Terraform, the AWS CLI, and your AWS credentials. No prior experience with any of these tools is required.
Install Terraform and AWS CLI
Before you can deploy infrastructure with code, you need two tools on your machine. Terraform reads your configuration files and communicates with cloud providers to create resources.
The AWS CLI handles authentication between your local machine and your AWS account. In this step, you'll install both tools and connect them to your credentials.
In this step, get ready to:
- Install Terraform via Homebrew.
- Install the AWS CLI.
- Configure your AWS credentials for Terraform to use.
Install Terraform
Terraform is the Infrastructure as Code tool that turns your configuration files into real cloud resources. You'll use it throughout this project to create and manage an EC2 instance on AWS.
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type Terminal and press Enter to open it.
- Check if Terraform is already installed by running:
terraform -version
✔️ I see version 1.2 or higher
Nice, Terraform is ready. Skip ahead to the next substep.
ⓧ I see an older version
Your Terraform version is below 1.2, which this project requires. Upgrade it by running these commands:
- Add the HashiCorp tap and upgrade Terraform by running:
brew tap hashicorp/tap
brew upgrade hashicorp/tap/terraform
- Verify the upgrade by running:
terraform -version
You should now see version 1.2 or higher.
Seeing an error about the formula not being installed?
If you installed Terraform through a method other than Homebrew, the upgrade command will not work. Run brew install hashicorp/tap/terraform instead to install a fresh copy via Homebrew.
help me upgrade Terraform to the latest version
ⓧ Command not found
Terraform is not installed yet. Install it using Homebrew by running these commands:
- Add the HashiCorp tap and install Terraform by running:
brew tap hashicorp/tap
brew install hashicorp/tap/terraform
What do these commands do?
The first command adds HashiCorp's package repository (called a "tap") to Homebrew. The second command installs Terraform from that repository.
- Verify the installation by running:
terraform -version
You should see a version number like 1.15.4.
Don't have Homebrew?
Homebrew is a package manager for macOS. Install it first by visiting brew.sh and following the one-line install command on the homepage. Then re-run the Terraform install commands above.
help me install Homebrew and Terraform
Install the AWS CLI
The AWS CLI lets you interact with AWS services from your terminal. Terraform uses the credentials stored by the AWS CLI to authenticate when creating resources in your account.
- Check if the AWS CLI is already installed by running:
aws --version
✔️ I see a version number
The AWS CLI is ready. Skip ahead to the next substep.
ⓧ Command not found
The AWS CLI is not installed yet. Install it using the official macOS package by running these commands:
- Download and install the AWS CLI package by running:
curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"
sudo installer -pkg AWSCLIV2.pkg -target /
What do these commands do?
The first command downloads the official AWS CLI installer package. The second command runs the installer with admin privileges. Your Mac will ask for your password when you run the sudo command.
- Verify the installation by running:
aws --version
You should see output starting with aws-cli/2 followed by version details.
Still seeing command not found?
Close your terminal window and open a new one. The installer adds the aws command to your PATH, but existing terminal sessions may not pick it up until restarted.
help me troubleshoot my AWS CLI installation
Configure AWS credentials
Terraform needs permission to create resources in your AWS account. You'll provide this by configuring the AWS CLI with your access credentials.
Where do I get AWS credentials?
You need an AWS account and an access key pair. If you don't have one yet, sign in to the AWS Console, navigate to IAM and create a new user with programmatic access. Save the Access Key ID and Secret Access Key that AWS generates for you.
Never share these credentials or commit them to version control. They grant full access to your AWS account.
- Configure your credentials by running:
aws configure
What will this command ask?
The aws configure command will prompt you for four values, one at a time. Enter each value and press Enter to move to the next prompt.
- AWS Access Key ID: paste your access key from the IAM console.
- AWS Secret Access Key: paste your secret access key from the IAM console.
- Default region name: type us-west-2.
- Default output format: type json.
- Verify your configuration is saved correctly by running:
aws configure list
You should see a table showing your profile, access key (masked as ****************XXXX), secret key (also masked), and region set to us-west-2.
Seeing <not set> for your credentials?
Make sure you pasted your Access Key ID and Secret Access Key correctly when prompted. Run aws configure again and re-enter your credentials carefully.
help me fix my AWS CLI configuration
Your tools are installed and your credentials are locked in. Next up, you'll write your first Terraform configuration files to define the infrastructure you want to create.
Write Your Terraform Configuration
Your tools are ready. Now it's time to write the configuration that tells Terraform exactly what to build in AWS.
Terraform uses its own language called HCL (HashiCorp Configuration Language) to describe infrastructure. You'll create two files: one for Terraform's own settings, and one for the actual AWS resources you want to create.
In this step, get ready to:
- Create a project directory and define the Terraform provider settings.
- Write the main configuration with a provider, data source, and EC2 resource.
- Format and validate the configuration.
Create your project directory and terraform.tf
Every Terraform project lives in its own directory. You'll create one on your Desktop, then open it in Cursor to start writing configuration files.
- Open your terminal by pressing Cmd+Space (macOS) or the Windows key (Windows) and typing Terminal.
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir learn-terraform-aws
cd learn-terraform-aws
- Confirm you're in the new folder by running:
pwd
You should see a path ending in /Desktop/learn-terraform-aws.
- Open Cursor by pressing Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type Cursor and press Enter to open it.
- In Cursor, click File in the top menu bar.
- Click Open Folder.
- Navigate to your Desktop, select the learn-terraform-aws folder, and click Open.
You should see the learn-terraform-aws folder name in the Explorer sidebar on the left.
Why two separate files?
Terraform reads all .tf files in a directory as one configuration. Splitting settings into terraform.tf (Terraform's own requirements) and main.tf (your actual infrastructure) keeps things organized as projects grow.
- In the Explorer sidebar, hover over the folder name and click the New File icon (a page with a + sign).
- Name the file terraform.tf and press Enter.
- Add the following code to terraform.tf:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.92"
}
}
required_version = ">= 1.2"
}
What does this code do?
- required_providers tells Terraform to download the AWS provider plugin from HashiCorp's registry. The ~> 5.92 constraint means any version from 5.92 up to (but not including) 6.0.
- required_version ensures anyone using this configuration has Terraform 1.2 or newer installed.
- Save terraform.tf by pressing Cmd+S (macOS) or Ctrl+S (Windows).
You should see terraform.tf listed in the Explorer sidebar.
Don't see the file in the sidebar?
Make sure you opened the learn-terraform-aws folder itself (not a parent folder). Go to File > Open Folder and select the correct folder on your Desktop.
Help me open my project folder in Cursor.
✔️ Awesome, I've got everything!
Great. Make sure you've saved the file before moving on.
ⓧ I'd like to double check the full code
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.92"
}
}
required_version = ">= 1.2"
}
Define your infrastructure in main.tf
This file describes what you actually want to build. You'll define where to deploy (the provider), how to find the right server image (a data source), and the server itself (a resource).
- In the Explorer sidebar, click the New File icon again.
- Name the file main.tf and press Enter.
- Add the provider and data source by pasting the following code into main.tf:
provider "aws" {
region = "us-west-2"
}
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"]
}
owners = ["099720109477"] # Canonical
}
What does this code do?
- The provider block tells Terraform to deploy resources in the us-west-2 AWS region (Oregon).
- The data block is a data source. Instead of hardcoding an AMI ID (which changes by region), it dynamically fetches the latest Ubuntu 24.04 image from Canonical's official account.
- The filter matches AMI names using a wildcard pattern, and most_recent = true ensures you always get the newest one.
- Add the EC2 instance resource below the data source in main.tf:
resource "aws_instance" "app_server" {
ami = data.aws_ami.ubuntu.id
instance_type = "t2.micro"
tags = {
Name = "learn-terraform"
}
}
What does this code do?
- resource "aws_instance" "app_server" declares an EC2 virtual machine. The first string is the resource type, and the second is your local name for it.
- ami = data.aws_ami.ubuntu.id references the AMI found by the data source above. This is how Terraform connects resources together.
- instance_type = "t2.micro" selects a small, free-tier-eligible instance size.
- The tags block gives your instance a friendly name that appears in the AWS Console.
- Save main.tf by pressing Cmd+S (macOS) or Ctrl+S (Windows).
You should now see both terraform.tf and main.tf in the Explorer sidebar.
Seeing a syntax highlighting error?
Make sure each block has matching opening { and closing } braces. HCL is sensitive to unmatched braces. Also check that string values are wrapped in double quotes.
Help me fix a syntax error in my Terraform file.
✔️ Awesome, I've got everything!
Great. Make sure you've saved the file before moving on.
ⓧ I'd like to double check the full code
provider "aws" {
region = "us-west-2"
}
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"]
}
owners = ["099720109477"] # Canonical
}
resource "aws_instance" "app_server" {
ami = data.aws_ami.ubuntu.id
instance_type = "t2.micro"
tags = {
Name = "learn-terraform"
}
}
Format and validate your configuration
Before deploying, you'll use two built-in Terraform commands to catch errors early. One auto-formats your code, and the other checks that everything is syntactically correct.
- Open the integrated terminal in Cursor by pressing Ctrl+` (backtick key, same on macOS and Windows).
- Format your configuration files by running:
terraform fmt
What does terraform fmt do?
This command rewrites your .tf files to follow HCL's canonical style (consistent indentation, aligned equals signs). If any files were reformatted, their names are printed. If nothing prints, your formatting was already correct.
- Validate your configuration by running:
terraform validate
You should see:
Success! The configuration is valid.
What does terraform validate do?
This command checks your configuration for syntax errors, missing required arguments, and incorrect attribute types. It does not connect to AWS or create anything. It only verifies that your code makes sense to Terraform.
Seeing an error instead of success?
If you see a "Missing required provider" error, you need to run terraform init first (covered in the next step). This downloads the AWS provider plugin that terraform validate needs to check your configuration.
If you see a syntax error, double-check your files against the full code tabs above. Make sure every block has matching braces and all strings use double quotes.
Help me fix my terraform validate error.
Your configuration is written and validated. Next up, you'll initialize Terraform and deploy your EC2 instance to the cloud with a single command.
Deploy Your Infrastructure
Your Terraform configuration is written, formatted, and validated. Now comes the exciting part: deploying a real EC2 instance to AWS with a single command.
Terraform follows a predictable workflow. It downloads provider plugins, generates an execution plan for your review, and only makes changes after you approve. This means you always know exactly what will happen before it happens.
In this step, get ready to:
- Initialize the Terraform workspace and download the AWS provider plugin.
- Apply your configuration to create a live EC2 instance on AWS.
- Inspect the Terraform state to see what resources are being tracked.
Initialize the Terraform workspace
Before Terraform can create any resources, it needs to download the provider plugins your configuration requires. The terraform init command handles this automatically.
- Initialize your workspace by running this command in your terminal:
terraform init
What does this command do?
This command reads your terraform.tf file, finds the hashicorp/aws provider you specified, and downloads it into a hidden .terraform directory.
It also creates a .terraform.lock.hcl file that locks the exact provider version. This ensures every future run uses the same version for consistency.
You should see output showing that Terraform is installing the AWS provider, followed by a success message confirming initialization is complete.
Seeing an error during init?
- Make sure you are inside the learn-terraform-aws directory. Terraform looks for configuration files in the current folder.
- Check your internet connection. Terraform needs to download the provider plugin from the HashiCorp registry.
- Verify that terraform.tf exists and has the correct provider source (hashicorp/aws).
Help me troubleshoot my terraform init error
Apply the configuration to create your instance
With the workspace initialized, you can now deploy your infrastructure. The terraform apply command generates an execution plan and asks you to approve it before making any changes.
- Deploy your EC2 instance by running:
terraform apply
What happens when you run this?
Terraform first generates an execution plan showing exactly what it will create. You will see a summary line that says Plan: 1 to add, 0 to change, 0 to destroy. This confirms Terraform will create one new resource (your EC2 instance).
Terraform then asks for your approval. Only typing yes will proceed. Any other input cancels the operation.
- Review the execution plan. Look for the + create symbol next to aws_instance.app_server. This means Terraform will create this resource.
- Type yes and press Enter to confirm the deployment.
Terraform will now create the EC2 instance. This takes about 30 seconds. You will see progress updates as the instance is being created, followed by a confirmation message when it completes.
What is Terraform state?
After applying, Terraform creates a terraform.tfstate file in your project directory. This file records the current state of your real infrastructure so Terraform can track what it manages.
The state file maps your configuration resources to real AWS resource IDs. Without it, Terraform would not know which cloud resources it created or how to update them later.
Apply failed with an error?
- If you see a credentials error, verify your AWS credentials are configured correctly by running aws configure list.
- If you see a permissions error, your IAM user may not have EC2 permissions. Make sure your user has the AmazonEC2FullAccess policy attached.
- If you accidentally typed something other than yes, Terraform cancels the operation. Run terraform apply again.
Help me fix my terraform apply error
Inspect your infrastructure state
Your EC2 instance is now live on AWS. Terraform tracks everything it created in its state file. You can query this state to see what resources exist and their details.
- List all resources Terraform is tracking by running:
terraform state list
You should see two entries: data.aws_ami.ubuntu (the AMI data source) and aws_instance.app_server (your EC2 instance).
- View the full details of your deployed resources by running:
terraform show
What does terraform show tell you?
This command prints the complete state of every resource Terraform manages. For your EC2 instance, you will see details like the instance ID, public IP address, private IP, availability zone, and the AMI it is running.
These are the same values you would see in the AWS Console if you navigated to your EC2 dashboard. The difference is that Terraform tracks them locally so it can detect changes and manage updates.
State list shows nothing?
- Make sure terraform apply completed successfully with the message confirming resources were added. If you cancelled the apply, no state file was created.
- Confirm you are in the learn-terraform-aws directory. The state file lives in the same folder as your configuration.
Help me understand why terraform state list is empty
Your EC2 instance is running on AWS, deployed entirely through code. Next up, you will modify your infrastructure and learn how to tear it all down with a single command.
Modify and Destroy Your Infrastructure
Your EC2 instance is live in AWS, deployed entirely through code. In a real project, infrastructure never stays static. You rename resources, change configurations, and eventually tear things down.
Terraform handles all of these changes intelligently. When you modify your configuration, Terraform calculates the minimum set of changes needed and applies only those. In this step, you'll modify your instance's tag, apply the change in-place, and then destroy everything to avoid any charges.
In this step, get ready to:
- Modify the instance's Name tag and preview the change with an execution plan.
- Apply the modification in-place without recreating the instance.
- Destroy all infrastructure to clean up your AWS account.
Change the instance's Name tag
Terraform tracks the desired state of your infrastructure in your configuration files. When you change a value, Terraform compares your updated config to the real-world state and figures out what needs to change.
- In main.tf, scroll down to the tags block inside your aws_instance resource.
- Find this line:
Name = "learn-terraform"
- Replace "learn-terraform" with "my-terraform-server", so the line now looks like this:
Name = "my-terraform-server"
- Save main.tf.
Now let's preview what Terraform will do with this change before actually applying it.
- Preview the proposed change by running this command:
terraform plan
You should see output showing a ~ symbol next to your instance, indicating an update in-place. The plan shows the tag changing from "learn-terraform" to "my-terraform-server".
What does update in-place mean?
Terraform classifies changes into two types. An in-place update modifies the existing resource without destroying it. A replacement destroys the old resource and creates a new one.
Changing a tag is a non-destructive change, so Terraform updates the existing instance. Changing something fundamental (like the AMI) would require a full replacement.
Seeing an error about no changes?
If Terraform says there are no changes, double-check that you saved main.tf after editing the tag. The new value should be "my-terraform-server" with the quotes included.
Help me figure out why terraform plan shows no changes.
Apply the change
The plan confirmed Terraform will only update the tag. It won't destroy and recreate your instance. Now apply the change for real.
- Apply the tag change by running this command:
terraform apply
- Terraform shows the same execution plan you just previewed. Type yes and press Enter to confirm.
You should see output confirming that 1 resource was changed. Terraform modified only the tag without touching the rest of your instance.
Why didn't Terraform recreate the instance?
Terraform compares your configuration to the current state file. Since only the tag value changed, Terraform issues an AWS API call to update the tag directly. The instance keeps its same ID, IP address, and running state.
Apply failed with an error?
If you see an authentication error, your AWS credentials may have expired. Run aws configure list to check your credentials are still set.
Help me fix my terraform apply error.
✔️ Awesome, I've got everything!
Great. Double check you've saved your file.
ⓧ I'd like to double check the full code
provider "aws" {
region = "us-west-2"
}
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"]
}
owners = ["099720109477"] # Canonical
}
resource "aws_instance" "app_server" {
ami = data.aws_ami.ubuntu.id
instance_type = "t2.micro"
tags = {
Name = "my-terraform-server"
}
}
Destroy all infrastructure
Your instance is running in AWS and could eventually cost money if left beyond the free tier. Terraform makes cleanup easy. One command tears down everything it created.
- Destroy all resources managed by Terraform by running this command:
terraform destroy
What does terraform destroy do?
This command reads your state file and terminates every resource Terraform is tracking. It shows you a destroy plan first (just like terraform apply shows a create/change plan) so you can review before confirming.
- Terraform displays the destroy plan showing 1 to destroy. Type yes and press Enter to confirm.
You should see output confirming that 1 resource was destroyed. Your EC2 instance is now terminated in AWS.
- Verify there are no resources left by running this command:
terraform state list
The command should return no output. This confirms Terraform's state file is empty and no resources are being tracked.
Destroy command failed?
If you see a timeout or dependency error, run terraform destroy again. Occasional network issues with AWS can cause temporary failures, but the command is safe to retry.
Help me fix my terraform destroy error.
You've completed the full Terraform lifecycle. You created infrastructure from code, modified it without downtime, and destroyed it in seconds. Next up, the Secret Mission challenges you to extend your configuration with new resource types.
Secret mission
Add Outputs and a Security Group
Your EC2 instance is deployed, but there is no way to reach it from the internet. In this secret mission, you will add a security group to open port 80 for HTTP traffic and create an output to display your instance's public IP address.
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. If you completed the final step, your AWS resources are already destroyed. The local project files and installed tools remain on your machine.
Cost warning
EC2 instances cost money if left running beyond the AWS Free Tier. If you re-applied your Terraform configuration and did not run terraform destroy, your instance is still running and accruing charges.
Resources you used:
- 1 EC2 instance (t2.micro, free tier eligible) in AWS.
- 1 security group (learn-terraform-web-sg) in AWS (if you completed the Secret Mission).
- Local project folder learn-terraform-aws on your Desktop.
- Terraform CLI installed via Homebrew.
- AWS CLI installed via the macOS pkg installer.
- AWS credentials stored in ~/.aws/.
Keep everything running
No action needed. Choose this if you plan to keep experimenting with Terraform and AWS.
- Your local learn-terraform-aws folder stays on your Desktop for future use.
- Terraform and AWS CLI remain installed and ready for your next project.
- Your AWS credentials in ~/.aws/ stay configured for future CLI commands.
- If you re-applied your Terraform configuration, remember that running EC2 instances may incur charges beyond the free tier.
Pause - I'll come back to this later
Terraform does not have a built-in pause command for EC2 instances. If you still have a running instance, destroy it to avoid charges. You can always re-create it later with terraform apply.
- If you have a running instance, navigate to your project folder and run:
- Your local files and installed tools remain in place for when you return.
cd ~/Desktop/learn-terraform-aws
terraform destroy
- Type yes when prompted to confirm.
Delete - I don't want to use this again
Remove all project resources, installed tools, and credentials to start fresh.
- Destroy any remaining AWS resources by running:
cd ~/Desktop/learn-terraform-aws
terraform destroy
- Type yes when prompted to confirm.
- Delete the local project folder by running:
cd ~/Desktop
rm -rf learn-terraform-aws
- Uninstall Terraform by running:
brew uninstall hashicorp/tap/terraform
- Uninstall the AWS CLI by running:
sudo rm /usr/local/bin/aws
sudo rm /usr/local/bin/aws_completer
sudo rm -rf /usr/local/aws-cli
- Remove your stored AWS credentials by running:
rm -rf ~/.aws/
What about my AWS account?
Your AWS account is free to keep. There are no charges for having an account with no running resources. You only pay for active services.
That's a Wrap!
That's a Wrap!
Nice work! You just deployed a live EC2 instance to AWS using nothing but Terraform configuration code.
You've learned how to:
- Written HCL configuration defining a provider, data source, and EC2 resource across a structured file layout.
- Executed the full Terraform workflow: init, validate, plan, apply, and destroy.
- Managed infrastructure state and applied in-place modifications to a running instance without recreating it.
- Added a security group and output values to extend your configuration.
Ready to quiz yourself?