Deploy an EC2 Instance with Terraform
Today we will test out Terraform and do a Basic Deployment by Launching an EC2 Instance on Amazon Web Services.
What is Terraform:
As described from their website:
"Terraform enables you to safely and predictably create, change, and improve production infrastructure. It is an open source tool that codifies APIs into declarative configuration files that can be shared amongst team members, treated as code, edited, reviewed, and versioned."
Download Terraform Binary:
Find the latest version here: https://www.terraform.io/downloads.html
$ wget -O terraform_0.9.9_linux_amd64.zip https://releases.hashicorp.com/terraform/0.9.9/terraform_0.9.9_linux_amd64.zip?_ga=2.180616622.843755498.1498543547-632150628.1498543547
$ unzip terraform_0.9.9_linux_amd64.zip
$ mv terraform /usr/local/bin/
$ terraform --version
Terraform v0.9.9
Create a Terraform Configuration:
In my basic example, I will be launching an t2.micro EC2 instance in my Default VPC in Dublin.
/opt/disk/terraform/terraform-ec2.tf
provider "aws" {
region = "eu-west-1"
alias = "dublin"
}
resource "aws_instance" "debug_instance" {
provider = "aws.dublin"
ami = "ami-d7b9a2b1"
instance_type = "t2.micro"
key_name = "MyKey"
vpc_security_group_ids = ["sg-1234abcde"]
subnet_id = "subnet-1234abcde"
tags {
Name = "Terraform Test with EC2"
}
}
Execution Plan: Determine the outcome of your Deployment:
If you are in the same directory you do not have to pass the path to the *.tf file, but since I am in my home directory, I will pass the path to my terraform-ec2.tf file
$ terraform plan /opt/disk/terraform/
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
The Terraform execution plan has been generated and is shown below.
Resources are shown in alphabetical order for quick scanning. Green resources
will be created (or destroyed and then created if an existing resource
exists), yellow resources are being changed in-place, and red resources
will be destroyed. Cyan entries are data sources to be read.
Note: You didn't specify an "-out" parameter to save this plan, so when
"apply" is called, Terraform can't guarantee this is what will execute.
+ aws_instance.debug_instance
ami: "ami-d7b9a2b1"
associate_public_ip_address: "<computed>"
availability_zone: "<computed>"
ebs_block_device.#: "<computed>"
ephemeral_block_device.#: "<computed>"
instance_state: "<computed>"
instance_type: "t2.micro"
ipv6_address_count: "<computed>"
ipv6_addresses.#: "<computed>"
key_name: "RuanBekker-EU"
network_interface.#: "<computed>"
network_interface_id: "<computed>"
placement_group: "<computed>"
primary_network_interface_id: "<computed>"
private_dns: "<computed>"
private_ip: "<computed>"
public_dns: "<computed>"
public_ip: "<computed>"
root_block_device.#: "<computed>"
security_groups.#: "<computed>"
source_dest_check: "true"
subnet_id: "subnet-abcd1234"
tags.%: "1"
tags.Name: "Terraform Test with EC2"
tenancy: "<computed>"
volume_tags.%: "<computed>"
vpc_security_group_ids.#: "1"
vpc_security_group_ids.4159630609: "sg-7e210318"
Plan: 1 to add, 0 to change, 0 to destroy.
Deploy: Apply your Deployment:
$ terraform apply /opt/disk/terraform/
aws_instance.debug_instance: Creating...
ami: "" => "ami-d7b9a2b1"
associate_public_ip_address: "" => "<computed>"
availability_zone: "" => "<computed>"
ebs_block_device.#: "" => "<computed>"
ephemeral_block_device.#: "" => "<computed>"
instance_state: "" => "<computed>"
instance_type: "" => "t2.micro"
ipv6_address_count: "" => "<computed>"
ipv6_addresses.#: "" => "<computed>"
key_name: "" => "MyKey"
network_interface.#: "" => "<computed>"
network_interface_id: "" => "<computed>"
placement_group: "" => "<computed>"
primary_network_interface_id: "" => "<computed>"
private_dns: "" => "<computed>"
private_ip: "" => "<computed>"
public_dns: "" => "<computed>"
public_ip: "" => "<computed>"
root_block_device.#: "" => "<computed>"
security_groups.#: "" => "<computed>"
source_dest_check: "" => "true"
subnet_id: "" => "subnet-1234abcd"
tags.%: "" => "1"
tags.Name: "" => "Terraform Test with EC2"
tenancy: "" => "<computed>"
volume_tags.%: "" => "<computed>"
vpc_security_group_ids.#: "" => "1"
vpc_security_group_ids.4159630609: "" => "sg-1234abcd"
aws_instance.debug_instance: Still creating... (10s elapsed)
aws_instance.debug_instance: Still creating... (20s elapsed)
aws_instance.debug_instance: Creation complete (ID: i-0039436a761dbb650)
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.
State path:
From our working directory we will have a state file:
$ ls
terraform.tfstate
The data will be stored as json, lets read the data from the state file:
$ cat terraform.tfstate
{
"version": 3,
"terraform_version": "0.9.9",
"serial": 0,
"lineage": "f410e287-50e1-4e56-b929-c2d000f0a08b",
"modules": [
{
"path": [
"root"
],
"outputs": {},
"resources": {
"aws_instance.debug_instance": {
"type": "aws_instance",
"depends_on": [],
"primary": {
"id": "i-0039436a761dbb650",
"attributes": {
"ami": "ami-d7b9a2b1",
"associate_public_ip_address": "true",
"availability_zone": "eu-west-1c",
"disable_api_termination": "false",
"ebs_block_device.#": "0",
"ebs_optimized": "false",
"ephemeral_block_device.#": "0",
"iam_instance_profile": "",
"id": "i-0039436a761dbb650",
"instance_state": "running",
"instance_type": "t2.micro",
"ipv6_addresses.#": "0",
"key_name": "MyKey",
"monitoring": "false",
"network_interface.#": "0",
"network_interface_id": "eni-1234abcd",
"primary_network_interface_id": "eni-1234abcd",
"private_dns": "ip-172-31-26-80.eu-west-1.compute.internal",
"private_ip": "172.31.26.80",
"public_dns": "ec2-34-253-6-43.eu-west-1.compute.amazonaws.com",
"public_ip": "34.253.6.43",
"root_block_device.#": "1",
"root_block_device.0.delete_on_termination": "true",
"root_block_device.0.iops": "100",
"root_block_device.0.volume_size": "8",
"root_block_device.0.volume_type": "gp2",
"security_groups.#": "0",
"source_dest_check": "true",
"subnet_id": "subnet-1234abcd",
"tags.%": "1",
"tags.Name": "Terraform Test with EC2",
"tenancy": "default",
"volume_tags.%": "0",
"vpc_security_group_ids.#": "1",
"vpc_security_group_ids.4159630609": "sg-1234abcd"
},
"meta": {
"e2bfb730-ecaa-11e6-8f88-34363bc7c4c0": {
"create": 600000000000,
"delete": 600000000000,
"update": 600000000000
},
"schema_version": "1"
},
"tainted": false
},
"deposed": [],
"provider": "aws.dublin"
}
},
"depends_on": []
}
]
}
Show: Using show to parse the state file to human readable output:
$ terraform show
aws_instance.debug_instance:
id = i-0039436a761dbb650
ami = ami-d7b9a2b1
associate_public_ip_address = true
availability_zone = eu-west-1c
disable_api_termination = false
ebs_block_device.# = 0
ebs_optimized = false
ephemeral_block_device.# = 0
iam_instance_profile =
instance_state = running
instance_type = t2.micro
ipv6_addresses.# = 0
key_name = MyKey
monitoring = false
network_interface.# = 0
network_interface_id = eni-1234abcd
primary_network_interface_id = eni-1234abcd
private_dns = ip-172-31-26-80.eu-west-1.compute.internal
private_ip = 172.31.26.80
public_dns = ec2-34-253-6-43.eu-west-1.compute.amazonaws.com
public_ip = 34.253.6.43
root_block_device.# = 1
root_block_device.0.delete_on_termination = true
root_block_device.0.iops = 100
root_block_device.0.volume_size = 8
root_block_device.0.volume_type = gp2
security_groups.# = 0
source_dest_check = true
subnet_id = subnet-8077d8e5
tags.% = 1
tags.Name = Terraform Test with EC2
tenancy = default
volume_tags.% = 0
vpc_security_group_ids.# = 1
vpc_security_group_ids.4159630609 = sg-1234abcd
Planning on Terminating the Deployment:
To check what will be terminated, we can use the -destroy argument on the plan command:
$ terraform plan -destroy /opt/disk/terraform/
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
aws_instance.debug_instance: Refreshing state... (ID: i-0039436a761dbb650)
The Terraform execution plan has been generated and is shown below.
Resources are shown in alphabetical order for quick scanning. Green resources
will be created (or destroyed and then created if an existing resource
exists), yellow resources are being changed in-place, and red resources
will be destroyed. Cyan entries are data sources to be read.
Note: You didn't specify an "-out" parameter to save this plan, so when
"apply" is called, Terraform can't guarantee this is what will execute.
- aws_instance.debug_instance
Plan: 0 to add, 0 to change, 1 to destroy.
Terminating the Deployment:
You can use the -force argument, no confirmations will be shown.
$ terraform destroy /opt/disk/terraform/
Do you really want to destroy?
Terraform will delete all your managed infrastructure.
There is no undo. Only 'yes' will be accepted to confirm.
Enter a value: yes
aws_instance.debug_instance: Refreshing state... (ID: i-0039436a761dbb650)
aws_instance.debug_instance: Destroying... (ID: i-0039436a761dbb650)
aws_instance.debug_instance: Still destroying... (ID: i-0039436a761dbb650, 10s elapsed)
aws_instance.debug_instance: Still destroying... (ID: i-0039436a761dbb650, 20s elapsed)
aws_instance.debug_instance: Still destroying... (ID: i-0039436a761dbb650, 30s elapsed)
aws_instance.debug_instance: Still destroying... (ID: i-0039436a761dbb650, 40s elapsed)
aws_instance.debug_instance: Still destroying... (ID: i-0039436a761dbb650, 50s elapsed)
aws_instance.debug_instance: Still destroying... (ID: i-0039436a761dbb650, 1m0s elapsed)
aws_instance.debug_instance: Still destroying... (ID: i-0039436a761dbb650, 1m10s elapsed)
aws_instance.debug_instance: Destruction complete
Destroy complete! Resources: 1 destroyed.
Resources: