Deploying Your First Server with Terraform: A Beginner’s Guide
We are moving forward in this 30 day terraform challenge and this is day 3.
In today’s blog we’re going to start having our hands dirty with creating and deploying our first server with terraform start from the previous infrastructure build using terraform , you can find it in my previous blog .
Let’s start !
In this Blog will try to build this infrastructure to deploy a single server inside AWS using terraform :
Steps
Step 1: Building the basic infrastructure without the server
- Ensure that you already build the infrastructure built already in the previous blog , you can access it from here : 30 Day Terraform Challenge day 2
Note : If you already build the infrastructure from day two , you have to redo the commands : terraform init
terraform plan
terraform apply
, to get it up and running again after it has been destroyed .
Step 2 : Show the current infrastructure
- On your workstation, navigate to the /workstation/terraform directory. To view the applied configuration utilize the
terraform show
command to view the resources created and find the IP address for your instance.
terraform show
Step 3: Update your Configuration to include EC2 instance
- Terraform can perform in-place updates after changes are made to the
main.tf
configuration file. Update yourmain.tf
to include an EC2 instance in the public subnet: - Append the following code to
main.tf
# Terraform Data Block - To Lookup Latest Ubuntu 20.04 AMI Image
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["099720109477"]
}
# Terraform Resource Block - To Build EC2 instance in Public Subnet
resource "aws_instance" "web_server" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
subnet_id = aws_subnet.public_subnets["public_subnet_1"].id
tags = {
Name = "Ubuntu EC2 Server"
}
}
- Save the configuration.
Step 4: Plan and Execute Changes
- Run a
terraform plan
to see the updates that Terraform needs to make.
terraform plan
- The output will look like this :
PS C:\Users\km_te\OneDrive\Documents\Terraform> terraform plan
data.aws_availability_zones.available: Reading...
data.aws_ami.ubuntu: Reading...
aws_eip.nat_gateway_eip: Refreshing state... [id=eipalloc-0647bd1d92a1a9a7e]
aws_vpc.vpc: Refreshing state... [id=vpc-01906a2ebf3ccbb18]
data.aws_availability_zones.available: Read complete after 0s [id=us-east-1]
data.aws_ami.ubuntu: Read complete after 1s [id=ami-057c5c24c7bbc7eb3]
aws_internet_gateway.internet_gateway: Refreshing state... [id=igw-05ccc3fcbd8bf151b]
aws_subnet.private_subnets["private_subnet_2"]: Refreshing state... [id=subnet-059ab2e26842eaf02]
aws_subnet.private_subnets["private_subnet_1"]: Refreshing state... [id=subnet-09b960cb77da75e35]
aws_subnet.private_subnets["private_subnet_3"]: Refreshing state... [id=subnet-01c7ff9982959a8dc]
aws_subnet.public_subnets["public_subnet_1"]: Refreshing state... [id=subnet-08f9b791cd447b460]
aws_subnet.public_subnets["public_subnet_2"]: Refreshing state... [id=subnet-07cb5abd5315b147a]
aws_subnet.public_subnets["public_subnet_3"]: Refreshing state... [id=subnet-076852d0955f06f48]
aws_route_table.public_route_table: Refreshing state... [id=rtb-05c5af95dddf6a882]
aws_nat_gateway.nat_gateway: Refreshing state... [id=nat-0968a4a888f6b5fef]
aws_route_table_association.public["public_subnet_3"]: Refreshing state... [id=rtbassoc-0ba04357250c04fa4]
aws_route_table_association.public["public_subnet_2"]: Refreshing state... [id=rtbassoc-0707661f74c97eb86]
aws_route_table_association.public["public_subnet_1"]: Refreshing state... [id=rtbassoc-01a0c5da3a96ab464]
aws_route_table.private_route_table: Refreshing state... [id=rtb-032db336a6e7e1028]
aws_route_table_association.private["private_subnet_1"]: Refreshing state... [id=rtbassoc-085d7b42f4b5227a0]
aws_route_table_association.private["private_subnet_3"]: Refreshing state... [id=rtbassoc-09a000704103d65af]
aws_route_table_association.private["private_subnet_2"]: Refreshing state... [id=rtbassoc-07418ce434c5a6ca6]
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.web_server will be created
+ resource "aws_instance" "web_server" {
+ ami = "ami-057c5c24c7bbc7eb3"
+ 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)
+ 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 = "t3.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 = "subnet-08f9b791cd447b460"
+ tags = {
+ "Name" = "Ubuntu EC2 Server"
}
+ tags_all = {
+ "Name" = "Ubuntu EC2 Server"
}
+ 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.
- Run a
terraform apply
to execute the updates that Terraform needs to make.
terraform 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:
- When prompted to apply the changes, respond with
yes
.
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.web_server: Creating...
aws_instance.web_server: Still creating... [10s elapsed]
aws_instance.web_server: Still creating... [20s elapsed]
aws_instance.web_server: Still creating... [30s elapsed]
aws_instance.web_server: Still creating... [40s elapsed]
aws_instance.web_server: Still creating... [50s elapsed]
aws_instance.web_server: Creation complete after 59s [id=i-0d544e90777ca8c2f]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Step 5: Show New State
- To view the applied configuration utilize the
terraform show
command to view the resources created. Look for theaws_instance.web_server
which is now present within Terraform's managed state. - Here is an example of the terraform state you will get :
# aws_instance.web_server:
resource "aws_instance" "web_server" {
ami = "ami-057c5c24c7bbc7eb3"
arn = "arn:aws:ec2:us-east-1:238447548277:instance/i-0169d1bad56113642"
associate_public_ip_address = true
availability_zone = "us-east-1b"
cpu_core_count = 1
cpu_threads_per_core = 2
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-0169d1bad56113642"
instance_initiated_shutdown_behavior = "stop"
instance_lifecycle = null
instance_state = "running"
instance_type = "t3.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-01b71bec6954803c4"
private_dns = "ip-10-0-101-50.ec2.internal"
private_ip = "10.0.101.50"
public_dns = null
public_ip = "3.88.172.134"
secondary_private_ips = []
security_groups = []
source_dest_check = true
spot_instance_request_id = null
subnet_id = "subnet-08f9b791cd447b460"
tags = {
"Name" = "Ubuntu EC2 Server"
}
tags_all = {
"Name" = "Ubuntu EC2 Server"
}
tenancy = "default"
user_data_replace_on_change = false
vpc_security_group_ids = [
"sg-06ee794766efde534",
]
capacity_reservation_specification {
capacity_reservation_preference = "open"
}
cpu_options {
amd_sev_snp = null
core_count = 1
threads_per_core = 2
}
credit_specification {
cpu_credits = "unlimited"
}
enclave_options {
enabled = false
}
maintenance_options {
auto_recovery = "default"
}
metadata_options {
http_endpoint = "enabled"
http_protocol_ipv6 = "disabled"
http_put_response_hop_limit = 1
http_tokens = "optional"
instance_metadata_tags = "disabled"
}
private_dns_name_options {
enable_resource_name_dns_a_record = false
enable_resource_name_dns_aaaa_record = false
hostname_type = "ip-name"
}
root_block_device {
delete_on_termination = true
device_name = "/dev/sda1"
encrypted = false
iops = 100
kms_key_id = null
tags = {}
tags_all = {}
throughput = 0
volume_id = "vol-015b8a6071400d3e2"
volume_size = 8
volume_type = "gp2"
}
}
- Run a
terraform state list
to list all of the items in Terraform's managed state.
PS C:\Users\km_te\OneDrive\Documents\Terraform> terraform state list
data.aws_ami.ubuntu
data.aws_availability_zones.available
aws_eip.nat_gateway_eip
aws_instance.web_server
aws_internet_gateway.internet_gateway
aws_nat_gateway.nat_gateway
aws_route_table.private_route_table
aws_route_table.public_route_table
aws_route_table_association.private["private_subnet_1"]
aws_route_table_association.private["private_subnet_2"]
aws_route_table_association.private["private_subnet_3"]
aws_route_table_association.public["public_subnet_1"]
aws_route_table_association.public["public_subnet_2"]
aws_route_table_association.public["public_subnet_3"]
aws_subnet.private_subnets["private_subnet_1"]
aws_subnet.private_subnets["private_subnet_2"]
aws_subnet.private_subnets["private_subnet_3"]
aws_subnet.public_subnets["public_subnet_1"]
aws_subnet.public_subnets["public_subnet_2"]
aws_subnet.public_subnets["public_subnet_3"]
aws_vpc.vpc
Step 6: Delete the AWS resources using Terraform to clean up our AWS environment
- To destroy your resources, execute the following command in the terminal. You should see Terraform refresh the state of each resource and subsequently destroy it in the proper order.
terraform destroy -auto-approve
- All the previously created resources will be deleted , for example for the EC2 instance :
and That’s it !
Resources used :
If you want to Access my Notes and the Flashcards I am building you can find them here and it’s going to be updated as long as I move forward in the challenge : https://shadowed-bubble-139.notion.site/30-Day-Terraform-Challenge-dec28b59677845218d72dd4163751c87?pvs=4
That’s all I have for today folks. Thank you for reading and/or following along! I hope this blog was helpful and worth your while. Stay tuned for my next project on this journey into the cloud.
Let’s connect on LinkedIn! 👉 https://www.linkedin.com/in/meriemterki/