Terraform How to create an EC2 instance on AWS
I have done practical tutorial in Create infrastructure | Terraform | HashiCorp Developer
Like to share the commands and usages with terraform to spin up an EC2 instance.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.92"
}
}
required_version = ">= 1.2"
}
provider "aws" {
region = "us-west-2"
access_key = "anaccesskey"
secret_key = "asecretkey"
s3_use_path_style = true
skip_credentials_validation = true
skip_metadata_api_check = true
skip_requesting_account_id = true
endpoints {
ec2 = "http://localhost:4566"
}
}
data "aws_ami" "linux" {
most_recent = true
filter {
name = "name"
values = ["amzn2-ami-hvm-2.0.*-x86_64-ebs"]
}
owners = ["137112412989"] # Amazon
}
resource "aws_instance" "app_server" {
ami = data.aws_ami.linux.id
instance_type = "t2.micro"
tags = {
Name = "hashicorp-learn"
}
}
Create infrastructure
*********************
To create your infrastructure, you must first use terraform init to install your configuration's required components
terraform cli
*************
# terraform fmt
# terraform init
Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 5.92"...
- Installing hashicorp/aws v5.100.0...
- Installed hashicorp/aws v5.100.0 (signed by HashiCorp)
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
#
Apply configuration
Terraform needs to authenticate with your cloud provider before it can make any changes. For this lab environment, the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables are already set. Terraform's AWS provider will automatically use these variables to authenticate with the AWS API when you use it to manage your infrastructure.
# aws configure list
Name Value Type Location
---- ----- ---- --------
profile <not set> None None
access_key ****************skey shared-credentials-file
secret_key ****************skey shared-credentials-file
region <not set> None None
#
Apply your configuration to create your EC2 instance. Terraform will print out a plan to make this change, and wait for you to confirm it. Respond to the confirmation prompt with a yes to confirm the plan.
********************************************************************************************************************************************************************************************************
# terraform apply
data.aws_ami.linux: Reading...
data.aws_ami.linux: Read complete after 4s [id=ami-022552c8354f3cb14]
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.app_server will be created
+ resource "aws_instance" "app_server" {
+ ami = "ami-022552c8354f3cb14"
+ arn = (known after apply)
+ associate_public_ip_address = (known after apply)
+ availability_zone = (known after apply)
+ cpu_core_count = (known after apply)
+ cpu_threads_per_core = (known after apply)
+ disable_api_stop = (known after apply)
+ disable_api_termination = (known after apply)
+ ebs_optimized = (known after apply)
+ enable_primary_ipv6 = (known after apply)
+ get_password_data = false
+ host_id = (known after apply)
+ host_resource_group_arn = (known after apply)
+ iam_instance_profile = (known after apply)
+ id = (known after apply)
+ instance_initiated_shutdown_behavior = (known after apply)
+ instance_lifecycle = (known after apply)
+ instance_state = (known after apply)
+ instance_type = "t2.micro"
+ ipv6_address_count = (known after apply)
+ ipv6_addresses = (known after apply)
+ key_name = (known after apply)
+ monitoring = (known after apply)
+ outpost_arn = (known after apply)
+ password_data = (known after apply)
+ placement_group = (known after apply)
+ placement_partition_number = (known after apply)
+ primary_network_interface_id = (known after apply)
+ private_dns = (known after apply)
+ private_ip = (known after apply)
+ public_dns = (known after apply)
+ public_ip = (known after apply)
+ secondary_private_ips = (known after apply)
+ security_groups = (known after apply)
+ source_dest_check = true
+ spot_instance_request_id = (known after apply)
+ subnet_id = (known after apply)
+ tags = {
+ "Name" = "hashicorp-learn"
}
+ tags_all = {
+ "Name" = "hashicorp-learn"
}
+ tenancy = (known after apply)
+ user_data = (known after apply)
+ user_data_base64 = (known after apply)
+ user_data_replace_on_change = false
+ vpc_security_group_ids = (known after apply)
+ capacity_reservation_specification (known after apply)
+ cpu_options (known after apply)
+ ebs_block_device (known after apply)
+ enclave_options (known after apply)
+ ephemeral_block_device (known after apply)
+ instance_market_options (known after apply)
+ maintenance_options (known after apply)
+ metadata_options (known after apply)
+ network_interface (known after apply)
+ private_dns_name_options (known after apply)
+ root_block_device (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: yes
aws_instance.app_server: Creating...
aws_instance.app_server: Still creating... [00m10s elapsed]
aws_instance.app_server: Creation complete after 10s [id=i-bfef111a643702f45]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
#
Inspect state
Terraform stores your workspace's state in a file named terraform.tfstate.
# pwd
/root/learn-terraform-aws-get-started
#
# pwd
/root/learn-terraform-aws-get-started
# ls -al
total 28
drwxr-xr-x 3 root root 4096 Sep 18 20:24 .
drwx------ 7 root root 4096 Sep 18 20:18 ..
drwxr-xr-x 3 root root 4096 Sep 18 20:18 .terraform
-rw-r--r-- 1 root root 1408 Sep 18 20:18 .terraform.lock.hcl
-rw-r--r-- 1 root root 0 Sep 18 20:07 main.tf
-rw-r--r-- 1 root root 809 Sep 18 20:17 terraform.tf
-rw-r--r-- 1 root root 6978 Sep 18 20:24 terraform.tfstate -----> this file contains workspace state in this file
List state
List the resources and data sources in your state with the terrafrom state list command.
# pwd
/root/learn-terraform-aws-get-started
# terraform state list
data.aws_ami.linux
aws_instance.app_server
#
#
Show state
Print out your workspace's entire state with terraform show.
# terraform show
# data.aws_ami.linux:
data "aws_ami" "linux" {
architecture = "x86_64"
arn = "arn:aws:ec2:us-west-2::image/ami-022552c8354f3cb14"
block_device_mappings = [
{
device_name = "/dev/xvda"
ebs = {
"delete_on_termination" = "false"
"encrypted" = "false"
"iops" = "0"
"snapshot_id" = "snap-a5672f21f82b1bcaf"
"throughput" = "0"
"volume_initialization_rate" = "0"
"volume_size" = "15"
"volume_type" = "standard"
}
no_device = null
virtual_name = null
},
]
boot_mode = "uefi"
creation_date = "2025-09-18T20:22:39.000Z"
deprecation_time = null
description = "Amazon Linux 2 AMI 2.0.20250818.2 x86_64 HVM ebs"
ena_support = false
hypervisor = "xen"
id = "ami-022552c8354f3cb14"
image_id = "ami-022552c8354f3cb14"
image_location = null
image_owner_alias = "amazon"
image_type = "machine"
imds_support = null
include_deprecated = false
kernel_id = null
last_launched_time = null
most_recent = true
name = "amzn2-ami-hvm-2.0.20250818.2-x86_64-ebs"
owner_id = "137112412989"
owners = [
"137112412989",
]
platform = null
platform_details = null
product_codes = []
public = true
ramdisk_id = null
root_device_name = "/dev/xvda"
root_device_type = "ebs"
root_snapshot_id = "snap-a5672f21f82b1bcaf"
sriov_net_support = null
state = "available"
state_reason = {
"code" = "UNSET"
"message" = "UNSET"
}
tags = {}
tpm_support = null
usage_operation = null
virtualization_type = "hvm"
filter {
name = "name"
values = [
"amzn2-ami-hvm-2.0.*-x86_64-ebs",
]
}
}
# aws_instance.app_server:
resource "aws_instance" "app_server" {
ami = "ami-022552c8354f3cb14"
arn = "arn:aws:ec2:us-west-2::instance/i-bfef111a643702f45"
associate_public_ip_address = true
availability_zone = "us-west-2a"
disable_api_stop = false
disable_api_termination = false
ebs_optimized = false
get_password_data = false
hibernation = false
host_id = null
iam_instance_profile = null
id = "i-bfef111a643702f45"
instance_initiated_shutdown_behavior = "stop"
instance_lifecycle = null
instance_state = "running"
instance_type = "t2.micro"
ipv6_address_count = 0
ipv6_addresses = []
key_name = null
monitoring = false
outpost_arn = null
password_data = null
placement_group = null
placement_partition_number = 0
primary_network_interface_id = "eni-b812cec48db0f7729"
private_dns = "ip-10-162-31-21.us-west-2.compute.internal"
private_ip = "10.162.31.21"
public_dns = "ec2-54-214-32-100.us-west-2.compute.amazonaws.com"
public_ip = "54.214.32.100"
secondary_private_ips = []
security_groups = []
source_dest_check = true
spot_instance_request_id = null
subnet_id = "subnet-f30ffd304d583bac5"
tags = {
"Name" = "hashicorp-learn"
}
tags_all = {
"Name" = "hashicorp-learn"
}
tenancy = "default"
user_data_replace_on_change = false
vpc_security_group_ids = []
metadata_options {
http_endpoint = "enabled"
http_protocol_ipv6 = "disabled"
http_put_response_hop_limit = 1
http_tokens = "optional"
instance_metadata_tags = "disabled"
}
root_block_device {
delete_on_termination = true
device_name = "/dev/sda1"
encrypted = false
iops = 0
kms_key_id = null
tags = {}
tags_all = {}
throughput = 0
volume_id = "vol-37651716444707f69"
volume_size = 8
volume_type = "gp2"
}
}
# terraform fmt
#