Deploying AWS EC2 Instances Using Terraform: Seamless Setup & Key Automation
Author

In this comprehensive guide, we will explore the steps to deploy EC2 instances on the renowned Amazon Web Services (AWS) platform using Terraform. Let’s dive in!

Setting the Stage: Prerequisites

Before diving into the world of Terraform and AWS, ensure you’re equipped with the following:

  • Terraform installed: Installed and ready on your device.
  • An active AWS account: If you don’t have one, you might want to sign up.
  • Configured IAM user: Finally, you’ll need an Identity and Access Management (IAM) user with the necessary permissions to create and manage AWS resources

Setting Up the Terraform Provider: provider.tf

In Terraform’s world, the provider.tf file kickstarts our journey. It’s where we tell Terraform which provider we want to use (AWS in this case), where to find it, and which version. After that, it’s all about granting permissions. You’ll need the access_key and secret_key of the IAM account, and the region.

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

# Configure the AWS Provider
provider "aws" {
    access_key = var.access_key
    secret_key = var.secret_key
    region     = var.region
}

main.tf

Automating SSH Key Pair Generation

In today’s era of automation, we no longer have to manually generate SSH key pairs for AWS EC2 with commands like ssh-keygen -t rsa -b 4096 and then upload the ssh public key. With tools like Terraform at our disposal, we can seamlessly automate this entire process, ensuring both efficiency and a smooth experience.

Let’s look at how we can dynamically generate an SSH key pair using the tls_private_key resource in Terraform.

​​resource "tls_private_key" "generated" {
  algorithm = "RSA"
  rsa_bits = 4096
}

With the code above:

  • The algorithm is set to “RSA“, which is the encryption algorithm for the key. In addition to the RSA algorithm, you can use other algorithms such as – ECDSA and ED25519 to generate the private key.
  • rsa_bits is set to 4096, which defines the size and the length of the generated RSA key.

Storing the SSH Public Key on AWS

After generating the key, the next step is to embed the public key in AWS to ensure secure and uninterrupted access to our EC2 instances. For this task, we turn to the Terraform’s aws_key_pair module. This module seamlessly registers the public key with AWS, eliminating the need for manual uploads or configuration.

resource "aws_key_pair" "generated_key" {
    key_name   = "tf_key_pair" 
    public_key = tls_private_key.generated.public_key_openssh
}

This configuration establishes an AWS key pair resource named “tf_key_pair” and uploads the public key from your local system to AWS. Here’s a breakdown:

  • key_name: This is the name given to the key pair on AWS. In our example, it’s called “aws_key_pair“. It’s good practice to give it a descriptive name so that you can easily identify it on AWS later.
  • public_key: tls_private_key.generated.public_key_openssh retrieves the public key from the “generatedkey that was created earlier using the tls_private_key resource. The .public_key_openssh attribute retrieves the public key in the OpenSSH format required by AWS.

Storing the SSH Private key locally

With our public key stored on AWS, the next step is to efficiently manage the private key. This key, your gateway to your EC2 instances, should be securely stored and easily accessible on your local machine. That’s where Terraform’s local_file resource comes in.

resource "local_file" "private_key" {
  content  = tls_private_key.generated.private_key_pem
  filename = "${path.module}/aws_key_pair.pem"
}

Breaking down this configuration:

  • content: This attribute holds the content intended for the file. In our scenario, it retrieves the private key in PEM format that was previously created using the tls_private_key resource.
  • filename: This attribute determines the location of the file on your system. Using ${path.module}, we’ll put the private key (aws_key_pair.pem) in the same directory as our active Terraform module.

Now that we have our SSH keys, it’s time to create our EC2 instance. To do this, we will use the aws_instance resource. This resource allows instances to be created, updated and deleted.

# Create an EC2 instance
resource "aws_instance" "ec2_instance" {
  ami           = var.ami_id
  instance_type = var.instance_type
  key_name      = aws_key_pair.generated_key.key_name

  # root disk
  root_block_device {
    volume_size           = var.volume_size 
    volume_type           = var.volume_type 
    encrypted             = false
    delete_on_termination = true
  }

  tags = {
    Name         = "Terraform EC2"
    Environment  = "DEV"
    Managed      = "IAC"
  }

  depends_on     = [ local_file.private_key ]
  
}

This configuration is broken down as follows:

  • ami : This attribute specifies the Amazon Machine Image (AMI) that will be used to start the instance.
  • instance_type: There are many instance types for specific use cases, some with more RAM, some with more GPU, so this determines which instance type is used.
  • key_name: This attribute refers to the SSH key pair we want our EC2 instance to use. We’ve linked it to the key we generated earlier using Terraform.
  • root_block_device: This block configures the root EBS volume, which is the primary storage device attached to the instance.
  • tags : Map of tags to assign to the resource.

📌 Pro Tip: If you’re testing, you can use t2.micro as the t2.micro instance type, and the us-west-2 region qualifies under the AWS compute free tier, so you won’t be charged as long as your account is less than 12 months old.

To fetch and display the public IP of the provisioned EC2 instance:

output "ec2_global_ips" {
  description = "The public IP address assigned to the instance"
  value       = ["${aws_instance.ec2_instance.*.public_ip}"]
}

This output block helps to display the public IP address of our EC2 instance once Terraform has applied the configurations. The syntax ${aws_instance.ec2_instance.*.public_ip} is Terraform’s way of referencing the public IP addresses of all instances created by the aws_instance.ec2_instance resource block.

Deploy EC2 Instance

Terraform Directory Initialization

Now that all our configuration is in place, we need to initialize terraform by downloading the providers. To do that, run terraform init from the directory in which the main.tf configuration file is located. Terraform downloads the AWS provider and installs it in a hidden subdirectory of your current working directory, called .terraform.

Run Terraform plan and apply

After successfully executing terraform init, we can run a terraform plan this will cause Terraform to create a Plan and show us what it will do.

Once you’re sure there are no unwanted changes, run the terraform apply command. When prompted, type “yes” to confirm. Terraform will then proceed to generate the SSH key pair resource and then create the EC2 instance resource and associate it with the corresponding SSH key.

With everything in place, you can now securely access the EC2 instance using the private key generated earlier. To initiate this connection, use the ssh command on your local machine:

ssh -i <path_modulemodule>/aws_key_pair.pem ec2-user@<instance_public_ip>

Cleanup

To delete the infrastructure that Terraform provisioned:

terraform destroy

The Power of Terraform in Cloud Era

In the cloud era, Terraform shines, offering top-notch automation capabilities. Moreover, as we’ve demonstrated, provisioning AWS EC2 instances is merely the beginning. So, the next time you think of manually working with AWS, remember: an efficient Terraform solution awaits you.