Elevating IaC Workflows with Spacelift Stacks and Dependencies 🛠️

Register for the July 23 demo →

Terraform

How to Create AWS EC2 Instance Using Terraform

How To Create An EC2 Instance On AWS Using Terraform

Terraform at Scale

Manage your infrastructure as it grows with a flexible, robust workflow, drift detection and reconciliation, and policies for security and compliance.

Book a demo

In this article, we will show how to create an EC2 instance on the Amazon Web Services (AWS) cloud platform using Terraform. We will detail the prerequisites, how to authenticate, how to set up your Terraform configuration files, and how to run through the Terraform lifecycle to initialize, plan, apply, and verify the deployment. We will also look at how to create multiple instances with different configuration values.

We will cover:

  1. Prerequisites
  2. Authentication with AWS
  3. How to create an EC2 instance using Terraform
  4. How to create multiple EC2 instances with different configuration

Prerequisites

To create an EC2 instance on AWS with Terraform, you’ll need to have the following prerequisites in place:

  1. AWS Account: You must have an AWS account to create and manage resources on the AWS cloud. If you don’t already have this, you can sign up for an account and use the free tier here. For the first 12 months, you can run a free EC2 instance of the following specifications:
  • 750 hours per month of Linux, RHEL, or SLES t2.micro or t3.micro instance dependent on region
  • 750 hours per month of Windows t2.micro or t3.micro instance dependent on region

If you continue to run your EC2 instance after the 12-month free tier allowance period is up you’ll start getting charged, so remember to clean up after the tutorial!

  1. Terraform installed: You’ll need to have Terraform installed on your machine to write and execute Terraform code. You can download the appropriate version for your machine here. Extract the downloaded zip file to a directory on your machine and add the directory containing the Terraform binary to your system’s PATH environment variable.
  2. AWS CLI installed & IAM User with permissions: You’ll need to have the AWS Command Line Interface (CLI) installed on your machine to interact with EC2 instances and other AWS resources from the command line. You can download the appropriate version for your system here. Once you have installed the AWS CLI, you can configure it by running the following command in a terminal window:
aws configure

This will prompt you to enter your AWS access key ID, secret access key, default region, and default output format. You can obtain your access key ID and secret access key from the AWS Management Console by navigating to the security credential section once logged in or create a new one from there if needed.

If you have not already done so, you should create an IAM user with the minimum required permissions necessary.

Learn more about AWS IAM best practices.

  1. SSH key pair: To access a Linux-based EC2 instance via SSH, you’ll need an SSH key pair.

Run the following command to generate a new SSH key pair:

ssh-keygen -t rsa -b 4096

This will create a new RSA key pair with a length of 4096 bits.

You will be prompted to enter a file name to save the key pair. The default location is in your user’s home directory under the .ssh directory. You can choose a different file name or directory if you prefer.

You will be prompted to enter a passphrase for the key pair. This is optional but recommended to add an extra layer of security.

The ssh-keygen command will generate two files: a private key file and a public key file. The private key file should be kept secure and never shared with anyone. The public key file can be shared with Amazon EC2 instances to allow SSH access.

Finally, to use the key pair with an Amazon EC2 instance, you must add the public key to the instance when you configure it with Terraform.

Authentication with AWS

You can configure Terraform to authenticate with AWS using several methods. With AWS CLI installed, we can use the named profiles method, which is a recommended approach for authenticating Terraform to AWS because it allows you to manage multiple sets of AWS credentials and control access to resources using IAM roles and policies.

First, ensure the AWS CLI is installed and configured using the guidelines in the prerequisite section to add the AWS access key ID and secret access key using the aws configure command. By default, the AWS CLI-named profiles use the same access key ID and secret access key as your default profile, so you don’t need to specify them again for different profiles.

Create an AWS CLI named profile for Terraform in the ~/.aws/config file (Linux and macOS) or %UserProfile%\.aws\config file (Windows). You can override the default access key ID and secret access key for a named profile by setting the aws_access_key_id and aws_secret_access_key attributes if required.

Although it is not necessary (and may cause problems if one of your team uses the code in a different SDK), you can add a new profile section with a unique profile name, such as jack.roper. The example below tells the AWS CLI to use the us-west-2 region for the jack.roper profile.

[profile jack.roper]
region = us-west-2

In your Terraform configuration file, the provider block can be configured as follows to reference the named profile:

provider "aws" {
  region = "us-west-2"
  profile = "jack.roper"
}

Note you can also authenticate using environment variables:

$ export AWS_ACCESS_KEY_ID=
$ export AWS_SECRET_ACCESS_KEY=

How to create an EC2 instance using Terraform

Example 1: How to create an EC2 instance using Terraform configuration files

Terraform configuration files contain the definitions of how to authenticate to AWS, and which resources you want Terraform to create and manage.

To create an EC2 instance on AWS in the simplest way using Terraform, create a file called main.tf and add the code from the example below.

Note the t2.micro instance type and the region of us-west-2 qualify under the AWS compute free tier so you will not be charged as long as your account is less than 12 months old. The AMI is of Amazon Linux 2 type.

main.tf

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.16"
    }
  }

  required_version = ">= 1.2.0"
}

provider "aws" {
  region  = "us-west-2"
  profile = "jack.roper"
}

resource "aws_instance" "example_server" {
  ami           = "ami-04e914639d0cca79a"
  instance_type = "t2.micro"

  tags = {
    Name = "JacksBlogExample"
  }
}

Initialize the Terraform directory

From the directory that contains the main.tf configuration file, run terrform init. Terraform downloads the aws provider and installs it in a hidden subdirectory of your current working directory, named .terraform.

Run terraform plan and apply

Run terraform plan. Terraform will create an execution plan.

Once you are happy that there are no unexpected changes, run terraform apply and enter yes to confirm the execution.

Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

   aws_instance.example_server will be created
  + resource "aws_instance" "example_server" {
      + ami                          = "ami-04e914639d0cca79a"
      + arn                          = (known after apply)
#...

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value:

Read more about the terraform plan command and terraform apply command.

Verify the deployment

Terraform will notify you on the command line once the EC2 instance is completed:

 Enter a value: yes

aws_instance.example_server: Creating...
aws_instance.example_server: Still creating... [10s elapsed]
aws_instance.example_server: Still creating... [20s elapsed]
aws_instance.example_server: Still creating... [30s elapsed]
aws_instance.example_server: Creation complete after 38s [id=i-01e03444e238b394]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Browse to the EC2 section in the AWS portal us-west-2 region and view your EC2 instance.

To avoid being charged, you can destroy the EC2 instance with terraform destroy.

Example 2: How to create an EC2 instance with user_data

Adding user_data is a way to add perform some configuration on the EC2 instance during creation, such as setting the hostname, mounting a file share, or installing a software package.

In this example, we will add our generated SSH key so we can connect to the EC2 instance we created earlier.

If you need to generate an SSH keypair first run the following:

ssh-keygen -t rsa -b 4096

I named my key jack1, so I can view the public portion of the key once created:

cat jack1.pub

And copy this key into my configuration file under the user_data section as follows:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.16"
    }
  }

  required_version = ">= 1.2.0"
}

provider "aws" {
  region  = "us-west-2"
  profile = "jack.roper"
}

resource "aws_instance" "example_server" {
  ami           = "ami-04e914639d0cca79a"
  instance_type = "t2.micro"
  user_data = <<EOF
#!/bin/bash
echo "Copying the SSH Key to the server"
echo -e "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDT54B8Le3cQe6ufDHltjSfq/VU1beEy5B2uhVZOGWbOekBhItqEmY3FErYHJzlHRWKwiwuH43uLpSlo/mvhYm/sV2zDWU/Sqq5Th2m9pUYGg0daFUA/iK3wBfWIVJHe6KqIEmLjKyoN3i12nTACbpmSTb5qXEnp6DVvdgIh3Pa9ID/r+geEeS0YIEztmyVKa947bp64/+zKXznWxyYmQYDZkmbKi8JsMXLGTdemQp6QBIme6D3KTPkGIFyG2VECRBn1InruQHeG+kmKDIAzxBeOfGFmTSDyEA+cT4+DMyQtWwcMx1mc9UAmGVo6NEwY1Y/mBOLHwdjBCnJO4Eiis3eJYiA8n7+jIAJ66ANPVIfBYoQ6NoYi2+Ep3EvhDcTJbq2/WgsJTwFAd84F+42PNsltnkTIRsOdsJZtrhxh1dgV91Sk919d0oME0Gph4XHk9q1ddD1lXRPfsG9Ejq6i9GqTB+spk6PXWaC57Im++XL/w3FI/sNLCIVgtXZeeL/GktzDrhDI2s+81hYTcyaw5cfdEb4xULS0NxLVUklO907gQsw4zU0zHYJHwN/uhsEn2eIuqECTFrF5ZmoJyyRygz5ddUKO4qVmWCzqUD0FTQLmYlmG97TSIFmUzVMhH+ZWd2knqlBfSHBUq2tex7fYxRRT9jIGHIfTgAXtbiBkucjlQ== jackw@JAC10" >> /home/ubuntu/.ssh/authorized_keys
EOF

  tags = {
    Name = "JacksBlogExample"
  }
}

Alternatively, you could use a key_name variable and create a key_pair with the public key beforehand, instead of directly inserting the public key into the user data section.

How to Create Multiple EC2 Instances With Different Configurations

To create multiple EC2 instances, you can use the for_each or count meta-arguments (which are mutually exclusive and cannot be used together).

For example, to simply create 10 instances of our EC2 machine with the same configuration, we set the count variable:

resource "aws_instance" "example_server" {
  ami           = "ami-04e914639d0cca79a"
  instance_type = "t2.micro"
  count         = 10

  tags = {
    Name = "JacksBlogExample"
  }
}

To create multiple EC2 instances with different configurations, we create a new file:

dev.tfvars

configuration = [
  {
    "application_name" : "example_app_server-dev",
    "ami" : "ami-04e914639d0cca79a",
    "no_of_instances" : "10",
    "instance_type" : "t2.medium",
  },
  {
    "application_name" : "example_web_server-dev",
    "ami" : "ami-04e914639d0cca79a",
    "instance_type" : "t2.micro",
    "no_of_instances" : "5"
  },
  
]

In this example, we will create 10 app servers of size t2.medium and 5 web servers of size t2.micro.

Our main.tf is modified as below:

main.tf

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.16"
    }
  }

  required_version = ">= 1.2.0"
}

provider "aws" {
  region  = "us-west-2"
  profile = "jack.roper"
}

variable "configuration" {
  description = "EC2 configuration"
  default = [{}]
}

locals {
  serverconfig = [
    for srv in var.configuration : [
      for i in range(1, srv.no_of_instances+1) : {
        instance_name = "${srv.application_name}-${i}"
        instance_type = srv.instance_type
        ami = srv.ami
      }
    ]
  ]
}

locals {
  instances = flatten(local.serverconfig)
}

resource "aws_instance" "example_server" {
  for_each = {for server in local.instances: server.instance_name =>  server}

  ami           = each.value.ami
  instance_type = each.value.instance_type
  user_data = <<EOF
#!/bin/bash
echo "Copying the SSH Key to the server"
echo -e "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDT54B8Le3cQe6ufDHltjSfq/VU1beEy5B2uhVZOGWbOekBhItqEmY3FErYHJzlHRWKwiwuH43uLpSlo/mvhYm/sV2zDWU/Sqq5Th2m9pUYGg0daFUA/iK3wBfWIVJHe6KqIEmLjKyoN3i12nTACbpmSTb5qXEnp6DVvdgIh3Pa9ID/r+geEeS0YIEztmyVKa947bp64/+zKXznWxyYmQYDZkmbKi8JsMXLGTdemQp6QBIme6D3KTPkGIFyG2VECRBn1InruQHeG+kmKDIAzxBeOfGFmTSDyEA+cT4+DMyQtWwcMx1mc9UAmGVo6NEwY1Y/mBOLHwdjBCnJO4Eiis3eJYiA8n7+jIAJ66ANPVIfBYoQ6NoYi2+Ep3EvhDcTJbq2/WgsJTwFAd84F+42PNsltnkTIRsOdsJZtrhxh1dgV91Sk919d0oME0Gph4XHk9q1ddD1lXRPfsG9Ejq6i9GqTB+spk6PXWaC57Im++XL/w3FI/sNLCIVgtXZeeL/GktzDrhDI2s+81hYTcyaw5cfdEb4xULS0NxLVUklO907gQsw4zU0zHYJHwN/uhsEn2eIuqECTFrF5ZmoJyyRygz5ddUKO4qVmWCzqUD0FTQLmYlmG97TSIFmUzVMhH+ZWd2knqlBfSHBUq2tex7fYxRRT9jIGHIfTgAXtbiBkucjlQ== jackw@JAC10" >> /home/ubuntu/.ssh/authorized_keys
EOF

  tags = {
    Name = "${each.value.instance_name}"
  }
}

output "instances" {
  value       = "${aws_instance.example_server}"
  description = "EC2 details"
}

Alternatively, you could use a map(object) and leave the type, the instance name in the tags would then be set as each.key.instance_name.

variable "configuration" {
  default = {
    instance1 = {
      ami = "ami…"
      instance_type = "t2.micro"
    }
  }
}

Check out also how to deploy an AWS ECS cluster with Terraform.

Key Points

You can create single or multiple instances of EC2 machines on AWS using Terraform with the same or different configuration. As you can see, Terraform is an extremely powerful way to deploy, manage and maintain your EC2 instances.

We encourage you also to explore how Spacelift makes it easy to work with Terraform. If you need any help managing your Terraform infrastructure, building more complex workflows based on Terraform, and managing AWS credentials per run, instead of using a static pair on your local machine, Spacelift is a fantastic tool for this. It supports Git workflows, policy as code, programmatic configuration, context sharing, drift detection, and many more great features right out of the box. You can check it for free by creating a trial account.

Note: New versions of Terraform will be placed under the BUSL license, but everything created before version 1.5.x stays open-source. OpenTofu is an open-source version of Terraform that will expand on Terraform’s existing concepts and offerings. It is a viable alternative to HashiCorp’s Terraform, being forked from Terraform version 1.5.6. OpenTofu retained all the features and functionalities that had made Terraform popular among developers while also introducing improvements and enhancements. OpenTofu is the future of the Terraform ecosystem, and having a truly open-source project to support all your IaC needs is the main priority.

Terraform Management Made Easy

Spacelift effectively manages Terraform state, more complex workflows, supports policy as code, programmatic configuration, context sharing, drift detection, resource visualization and includes many more features.

Start free trial

How can Spacelift stacks & dependencies elevate your IaC workflows?

Don’t miss our July 23 webinar.

Register for the webinar