Task

Create EC2 Instance with Dynamic AMI ID

Terraform · 15 min · Practical

Query AWS programmatically for the latest Amazon Linux AMI using Terraform data sources. Automatically attach custom local SSH key pairs, security group firewall rules, and expose the EC2 public IP dynamically.

Core Implementation Highlights

Dynamic AMI Lookupdata "aws_ami" "amazonlinux"
SSH Key Bindingfile("/home/ak/.ssh/id_rsa.pub")
Security Group FirewallSSH (22 Ingress) & All Egress
Output Public IPaws_instance.webserver.public_ip

The Complete Terraform Code

Save the code below into main.tf. It dynamically fetches the newest Amazon Linux AMI, creates an SSH Key Pair from your public key, provisions a Security Group for SSH access, creates a t2.micro EC2 instance, and outputs its public IP address.

main.tf
# Query AWS for the latest Amazon Linux AMI (Dynamic AMI ID)
data "aws_ami" "amazonlinux" {
  most_recent = true
  owners      = ["137112412989"]

  filter {
    name   = "architecture"
    values = ["x86_64"]
  }
}

# Register local SSH public key with AWS
resource "aws_key_pair" "shankey" {
  key_name   = "serverkey"
  public_key = file("/home/ak/.ssh/id_rsa.pub")
}

# Security Group Firewall allowing SSH (Port 22) Ingress & All Egress
resource "aws_security_group" "webserver_ig" {
  name = "webserver_ig"

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

# Provision EC2 Instance using Dynamic AMI ID
resource "aws_instance" "webserver" {
  ami                         = data.aws_ami.amazonlinux.id
  instance_type               = "t2.micro"
  associate_public_ip_address = true
  vpc_security_group_ids      = [aws_security_group.webserver_ig.id]
  key_name                    = aws_key_pair.shankey.key_name
}

# Output the assigned public IP address of the server
output "aws_ec2_name" {
  value = aws_instance.webserver.public_ip
}

Resource-by-Resource Explanation

data "aws_ami" "amazonlinux"

Instead of hardcoding a static AMI ID (which changes by region and gets outdated), Terraform queries the AWS API dynamically at runtime to fetch the latest official Amazon Linux image.

  • most_recent = true: Selects the newest image matching filters.
  • owners = ["137112412989"]: Amazon's official AWS account ID for official AMIs.
  • filter { name = "architecture" values = ["x86_64"] }: Narrows down query to 64-bit x86 hardware.
main.tf
data "aws_ami" "amazonlinux" {
  most_recent = true
  owners      = ["137112412989"]

  filter {
    name   = "architecture"
    values = ["x86_64"]
  }
}

aws_key_pair.shankey

Registers your local SSH public key with AWS under the name serverkey. The built-in file() function reads your public key from disk at /home/ak/.ssh/id_rsa.pub.

main.tf
resource "aws_key_pair" "shankey" {
  key_name   = "serverkey"
  public_key = file("/home/ak/.ssh/id_rsa.pub")
}

aws_security_group.webserver_ig

Acts as a virtual firewall for your EC2 server:

  • Ingress: Allows SSH connection over TCP port 22 from any IP address (0.0.0.0/0).
  • Egress: Allows all outbound traffic (protocol -1) to any destination so your instance can update packages and reach the internet.
main.tf
resource "aws_security_group" "webserver_ig" {
  name = "webserver_ig"

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

aws_instance.webserver

Provisions the EC2 virtual machine linking the dynamic AMI ID, SSH Key, and Security Group:

  • ami = data.aws_ami.amazonlinux.id: Injects the dynamic AMI ID returned by the data source.
  • instance_type = "t2.micro": Selects AWS Free-Tier eligible instance sizing.
  • associate_public_ip_address = true: Requests a public IPv4 address.
  • vpc_security_group_ids & key_name: Binds the security group and SSH key created above.
main.tf
resource "aws_instance" "webserver" {
  ami                         = data.aws_ami.amazonlinux.id
  instance_type               = "t2.micro"
  associate_public_ip_address = true
  vpc_security_group_ids      = [aws_security_group.webserver_ig.id]

  key_name = aws_key_pair.shankey.key_name
}

output "aws_ec2_name"

Prints the newly assigned public IP address directly in your terminal output after applying the configuration, allowing you to instantly SSH into the instance with ssh -i ~/.ssh/id_rsa ec2-user@<PUBLIC_IP>.

main.tf
output "aws_ec2_name" {
  value = aws_instance.webserver.public_ip
}

Deployment & Execution Commands

Run the standard Terraform lifecycle commands to validate, plan, and apply your dynamic infrastructure:

Terminal
# 1. Initialize AWS Provider plugins
$ terraform init

# 2. Format and validate configuration syntax
$ terraform fmt
$ terraform validate

# 3. Preview execution plan (fetches dynamic AMI)
$ terraform plan

# 4. Provision infrastructure in AWS
$ terraform apply -auto-approve

# 5. SSH into your new server
$ ssh -i ~/.ssh/id_rsa ec2-user@$(terraform output -raw aws_ec2_name)

# 6. Clean up resources when finished
$ terraform destroy -auto-approve