DEV Community

Kalio Princewill
Kalio Princewill

Posted on

Provisioning an AWS EC2 Instance with Terraform

Introduction

Infrastructure as Code (IaC) is becoming a key part of modern cloud engineering. It lets developers and DevOps engineers provision and manage cloud resources consistently and efficiently. Among the most popular IaC tools, Terraform stands out for its declarative syntax, strong community support, and multi-cloud capabilities.

This article provides a step-by-step guide to help you provision your first EC2 instance on AWS using Terraform. Whether you're a beginner exploring Terraform for the first time or someone looking to solidify your AWS provisioning workflow, this guide walks you through the entire process, from setting up your AWS environment to writing and applying Terraform configuration files. By the end, you'll understand how to securely configure access, provision an EC2 instance, and clean up resources while adhering to best practices.

Prerequisite 

To get the most out of this article, you must have the following:

Setting Up Your AWS Environment

Before Terraform can provision resources on AWS, it needs to authenticate with your AWS account. You'll generate an Access Key ID and a Secret Access Key using AWS Identity and Access Management (IAM) for this purpose. Follow these steps:

Create an IAM User

To securely manage access to your AWS resources, begin by creating a dedicated IAM user that Terraform will use for authentication. This will ensure that you avoid using your root account credentials.

1.Log in to your AWS Management Console. In the search bar, type IAM and select the IAM service.

Image description

2.On the left sidebar, click on Users, then select Create User.

Image description

3.Enter a preferred username (e.g.terraform-admin) and click Next.

4.On the Set Permissions page, attach the AdministratorAccess policy directly to the user. For this tutorial, we're using AdministratorAccess for simplicity, but in a production environment, always apply the Principle of Least Privilege by granting only the minimum necessary permissions. If you already have an IAM group with the required permissions, you can add the new user to that group instead.

5.Review the permissions, click Next, and then Create User. Your IAM user is now set up.

Create an Access Key

With the new IAM user in place, the next step is to generate the Access Key ID and Secret Access Key, which will allow Terraform to authenticate and interact with AWS services.

1.Navigate back to the IAM service and click on Users in the left sidebar.

2.Select the newly created user (terraform-admin) to open its details page.

3.Go to the Security Credentials tab.

4.In the Access Keys section, click on Create Access Key.

5.When prompted to select a use case, choose Command Line Interface (CLI).

6.Copy and securely store the Access Key ID and Secret Access Key displayed. After this step, AWS will not show the secret key again.

Configure AWS CLI on Your Local Machine

The next step is to configure your local machine to authenticate with AWS using the AWS Command Line Interface (CLI). This allows Terraform to communicate with AWS services seamlessly.

1.Open your terminal.

2.Run the following command on your terminal


aws configure

Enter fullscreen mode Exit fullscreen mode

3.Enter the following details when prompted:

  • AWS Access Key ID: Paste the Access Key ID you copied.

  • AWS Secret Access Key: Paste the Secret Access Key you copied.

  • Default region name: Enter us-east-1 (or your preferred AWS region).

  • Default output format: Enter json.

Your local machine is now configured to authenticate with AWS using the AWS CLI, allowing Terraform to communicate seamlessly with AWS services.

Creating your Terraform configuration for EC2 deployment

This section guides you through writing your Terraform configuration to provision an EC2 instance on AWS. You'll set up your project, define the AWS provider, create an SSH key pair, configure a security group, define your EC2 instance, and add useful outputs and variables.

Project Setup

1.Create a new directory for your Terraform project. Open your terminal and run:


mkdir terraform-project && cd terraform-project

Enter fullscreen mode Exit fullscreen mode

Generate an SSH Key Pair

To securely connect to your EC2 instance using SSH, you need an SSH key pair. You'll generate this on your local machine and then upload the public key to AWS through Terraform.

1.Open your terminal.

2.Generate a new SSH key pair by running:


ssh-keygen -t ed25519 -f ~/.ssh/my-ec2-key -C "my-ec2-key"

Enter fullscreen mode Exit fullscreen mode

This command creates an Ed25519 key pair, which is a modern and highly secure.

  • -f ~/.ssh/my-ec2-key flag specifies the file path and name for your private key (my-ec2-key) and its corresponding public key (my-ec2-key.pub).

  • -C "my-ec2-key" flag adds a descriptive comment to the key, making it easier to identify later.

3.When prompted for a passphrase, you can press Enter to leave it blank for simplicity in this tutorial. However, in a production environment, always use a strong passphrase to protect your private key.

This command generates two files in your ~/.ssh directory:

  • my-ec2-key: This is your private key. Keep this file absolutely secure and never share it.

  • my-ec2-key.pub: This is your public key. You will upload the contents of this file to AWS through Terraform.

Defining Your Terraform Configuration Files

To provision the EC2 instance, you'll organise your Terraform configuration into several core files, each serving a specific purpose:

Define Terraform Variables

Variables make your Terraform configurations flexible and reusable. Define them upfront to easily customise your deployment without altering the main resource definitions.

  1. Create a new file named variables.tf in your terraform-project directory.

  2. Add the following content to variables.tf:


variable  "aws_region" {

 description =  "The AWS region to deploy resources in."

 type =  string

 default =  "us-east-1"  # Set your default region here

}

variable  "instance_type" {

 description =  "The type of EC2 instance."

 type =  string

 default =  "t2.micro"  # Set your default instance type here

}

Enter fullscreen mode Exit fullscreen mode
  • aws_region: Specifies the AWS region for resource deployment.

  • instance_type: Defines the type of EC2 instance to launch.

Define the AWS Provider and Key Pair

This file will configure Terraform to interact with AWS and upload your generated public SSH key to the AWS Key Pairs service.

  1. Create a new file named provider.tf in your terraform-project directory.

  2. Add the following content to provider.tf:


terraform {

   required_providers {

     aws = {

       source  = "hashicorp/aws"

       version = "~> 5.0"  # Specify a compatible version (e.g., 5.x)

     }

   }

 }

  provider "aws" {

   region = var.aws_region  # Use the region defined in variables.tf

 }

  resource "aws_key_pair" "ec2_key" {

   key_name   = "my-ec2-key"  # This is the name AWS will use for your key

   public_key = file("~/.ssh/my-ec2-key.pub")  # Path to your public key file

 }

Enter fullscreen mode Exit fullscreen mode
  • terraform block: Declares the required AWS provider and its version.

  • provider "aws" block: Configures the AWS provider, using the aws_region variable for consistency.

  • resource "aws_key_pair": This resource uploads the content of your my-ec2-key.pub file to AWS, making it available for new EC2 instances. The key_name is how you'll refer to it in AWS.

Create the Security Group

A security group acts as a virtual firewall for your EC2 instance, controlling both inbound (ingress) and outbound (egress) network traffic.

1.Create a new file security_group.tf in your terraform-project directory.
2.Add the following configuration to security_group.tf:


resource  "aws_security_group"  "ec2_security_group" {

name= "terraform-ec2-sg"

description = "Allow SSH and HTTP access for Terraform EC2 instance"

ingress {

  description = "Allow SSH from my IP"

  from_port = 22

  to_port = 22

  protocol= "tcp"

  cidr_blocks = ["0.0.0.0/0"]

}

ingress {

  description = "Allow HTTP from anywhere"

  from_port = 80

  to_port = 80

  protocol= "tcp"

  cidr_blocks = ["0.0.0.0/0"]

}

egress {

  from_port = 0

  to_port = 0

  protocol= "-1"  # Allow all outbound protocols

  cidr_blocks = ["0.0.0.0/0"]

}

tags = {

  Name  =  "Terraform-EC2-SecurityGroup"

}

}

Enter fullscreen mode Exit fullscreen mode
  • ingress blocks: Define inbound rules.

  • egress block: Allows all outbound traffic from the instance.

  • tags: Apply a tag for easy identification in the AWS console.

Define the EC2 Instance and AMI Data Source

This main.tf file will contain the core definition of your EC2 instance. It also includes a data source to dynamically fetch the latest Ubuntu AMI.

1.Create a new file main.tf in your terraform-project directory.

2.Add the following content to main.tf:


# Data source to get the latest Ubuntu AMI

data  "aws_ami"  "latest_ubuntu_ami" {

most_recent = true

filter {

  name = "name"

  values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]

}

filter {

  name = "virtualization-type"

  values = ["hvm"]

}

owners = ["099720109477"] # Canonical's AWS account ID

}

# Define the EC2 instance resource

resource  "aws_instance"  "my_first_ec2" {

ami = data.aws_ami.latest_ubuntu_ami.id  # Use the dynamically fetched AMI ID

instance_type = var.instance_type # Use the instance type defined in variables.tf

key_name= aws_key_pair.ec2_key.key_name # Reference the key pair created in provider.tf

vpc_security_group_ids = [aws_security_group.ec2_security_group.id] # Attach the security group

tags = {

  Name  =  "MyFirstTerraformEC2"

}

}
Enter fullscreen mode Exit fullscreen mode
  • data "aws_ami": This data source automatically retrieves the ID of the most recent Ubuntu 22.04 LTS AMI.

  • resource "aws_instance": This block defines the EC2 instance itself, using the ami from the data source, instance_type from variables, and referencing the key_name and security_group created in their respective files.

Define the Outputs

Outputs are useful for displaying important information about your deployed resources after Terraform applies the configuration.

1.Create a new file outputs.tf in your terraform-project directory.

2.Add the following content to outputs.tf:


output "instance_public_ip" {

 description = "Public IP address of the EC2 instance."

 value       = aws_instance.my_first_ec2.public_ip

}

Enter fullscreen mode Exit fullscreen mode

These outputs will display the public IP address of your EC2 instance once it's provisioned, making it easy to connect to your instance.

Initialising and Applying Your Configuration

After writing your Terraform configuration files, the next step is to initialise your working directory, validate your code, review the execution plan, and finally apply the changes to provision your AWS resources.

Initialise Your Terraform Project

The terraform init command prepares your project's working directory by downloading necessary provider plugins and setting up the backend for state management. Run this command first whenever you start a new Terraform project or clone an existing one.

1.Open your terminal and navigate to your project directory if you are not already there:


cd terraform-project

Enter fullscreen mode Exit fullscreen mode

2.Run the initialisation command


terraform init

Enter fullscreen mode Exit fullscreen mode

Validate Your Terraform Configuration

Validating your Terraform code before attempting to create resources is a good practice. The terraform validate command checks your configuration for syntax errors and internal consistency.

To validate your configuration, run:


terraform validate

Enter fullscreen mode Exit fullscreen mode

If there are any errors, Terraform will provide detailed messages to help you fix them.

Review the Execution Plan

Before making any changes to your AWS environment, always generate and review an execution plan using terraform plan. This command shows you exactly what Terraform will create, modify, or destroy.


terraform plan

Enter fullscreen mode Exit fullscreen mode

Terraform compares your desired state (defined in your .tf files) with the current state of your AWS resources and proposes a set of changes to achieve the desired state.

Apply Your Terraform Configuration

Once you are satisfied with the execution plan, use terraform apply to provision the resources in your AWS account.

Apply the configuration to provision resources, run:


terraform apply
Enter fullscreen mode Exit fullscreen mode

This command executes the actions outlined in the terraform plan. Terraform will interact with the AWS API to create, update, or delete resources as necessary.

[

Verifying Your EC2 Instance

Confirm your EC2 instance is active in your AWS account following a successful terraform apply. This tutorial focuses on verification using the AWS Console, though the AWS CLI is also an option.

The AWS Management Console provides a visual way to confirm your instance's status.

1.Log in to your AWS Console.

2.In the search bar, type EC2 and select the EC2 service when it appears. This will take you to the EC2 Dashboard.

3.On the left-hand navigation pane, under Instances, click on Instances.

4.Locate your newly created EC2 instance. You should see an instance with the Name tag MyFirstTerraformEC2 (or whatever name you assigned in your main.tf).

5.Check the Instance state column. It should show Running.

6.Select your instance and review its details in the lower pane, including its Public IPv4 address, Instance ID, and associated Security Groups, to confirm everything matches your Terraform configuration.

Cleaning up your provisioned resources

After you've finished experimenting with your EC2 instance, it's crucial to clean up the resources you've provisioned. This prevents unnecessary AWS charges and keeps your account tidy. Terraform makes this process simple with a single command.

The terraform destroy command removes all resources defined in your current Terraform configuration.

1.Open your terminal.

2.Navigate to your project directory if you are not already there:

    cd terraform-project
Enter fullscreen mode Exit fullscreen mode

3.Initiate the destruction process:

terraform destroy

Enter fullscreen mode Exit fullscreen mode

Terraform will calculate all the resources that it previously created and are still managed by your configuration, and it will propose to destroy them. This includes your EC2 instance, security group, and the uploaded key pair.

And the instance has been terminated when verified on AWS Console.

Best practices for working with Terraform 

You've successfully provisioned and destroyed your first EC2 instance with Terraform! As you continue your Infrastructure as Code journey, consider these best practices and areas for further exploration.

Version Control

Always store your Terraform configuration files (.tf files) in a version control system like Git. This allows you to track changes, collaborate with others, and easily revert to previous versions if needed.

Git Ignore Sensitive Files

Prevent sensitive information and Terraform's internal working files from being committed to your Git repository. Create a .gitignore file in your project root and add the following entries:

# .gitignore

.terraform/

*.tfstate

*.tfstate.backup

.terraform.lock.hcl

Enter fullscreen mode Exit fullscreen mode
  • .terraform contains downloaded provider plugins and other internal Terraform files. You don't need to commit these.

  • *.tfstate is your Terraform state file. If you're not using remote state (which is recommended for teams), keep this out of version control as it can contain sensitive information and is crucial for Terraform's operation.

  • *.tfstate.backup backup state files don't need to be pushed as well.

  • .terraform.lock.hcl locks provider versions to ensure consistent builds, but it's typically fine to commit. The outline asks to ignore it if not using remote state, so I've included it.

Security

Security is paramount in cloud environments.

  • Never commit sensitive data directly: Avoid hardcoding AWS access keys or other secrets directly into your .tf files, especially if your repository is public or shared. Instead, use secure methods like AWS environment variables (as leveraged in this tutorial), AWS Secrets Manager, or a dedicated secret management tool like HashiCorp Vault.

  • Principle of Least Privilege: Always grant Terraform (or the IAM user/role it uses) only the minimum necessary permissions to perform its tasks. For this tutorial, we used AdministratorAccess for simplicity, but for production environments, define specific IAM policies that restrict actions to only the required resources.

Modularization

As your infrastructure grows, your Terraform configurations can become large and complex. Consider breaking down your configurations into reusable modules. Modules allow you to encapsulate and reuse common infrastructure patterns (e.g., a standard VPC setup, a common EC2 instance configuration) across different projects or environments.

Remote State

For team collaboration and production deployments, use remote state backends (e.g., Amazon S3, HashiCorp Cloud Platform, Terraform Cloud). Remote state safely stores your terraform.tfstate file in a shared, versioned, and often encrypted location. This enables multiple team members to work on the same infrastructure simultaneously without state conflicts and provides a reliable source of truth for your infrastructure's current state.

Conclusion

In this tutorial, you have successfully set up your AWS environment, written your first Terraform configuration, provisioned an EC2 instance, checked that it was deployed, and cleaned up the resources. You've experienced the core Terraform workflow: initialising your project, planning changes, applying them to create infrastructure, and finally, destroying it to avoid unintended costs. This exercise has equipped you with the building blocks for managing AWS resources using Infrastructure as Code (IaC).

The steps covered, from IAM user creation and SSH key management to defining resources like security groups and EC2 instances, form the bedrock of more complex deployments. As you continue to explore Terraform, remember the best practices discussed: leverage version control, secure your sensitive data, adhere to the principle of least privilege, and consider modularising your code and using remote state for collaborative or production environments

Top comments (0)