Lab 3 : Getting Started with Terraform
In this lab you are going to learn how to, * Create a Terraform Configuration * Add and initialise a Cloud Provisioner * Write a Resource by using HCL * Validate Configurations * Create a Change Plan * Apply Configurations to Build Actual Infrastructure * Modify Existing Infrastructure created with Terraform * Destroy the Infrastructure created by Terraform
Getting Started with terraform
CLI
You could get started with terraform
and check the list of commands for Terraform by running,
terrform -h
From the commands listed , the following are most commonly used ones,
terraform init
terraform validate
terraform plan
terraform apply
terraform destroy
Adding Terraform Configurations
mkdir terraform-code
cd terraform-code
You will begin by defining required_providers block for terraform. This defines which providers should terraform install, from where, and which version.
Create main.tf
with the following initial configuration
terraform {
required_providers {
aws = {
source = "registry.terraform.io/hashicorp/aws"
version = "~> 4.29"
}
}
}
Provider Source Components
You could read more about Provider Requirements at Provider Requirements - Configuration Language | Terraform by HashiCorp and about AWS Provider’s Version etc. used above at https://registry.terraform.io/providers/hashicorp/aws/latest.
Check the providers configuration using
terraform providers
Adding AWS Cloud Provider
In Terraform, Providers are responsible for understanding API interactions from * IaaS : e.g. AWS, Azure, GCP * PaaS : e.g. Heroku, Kubernetes * SaaS : e.g. CloudFlare, DNSSimple, DNSMadeEasy
Let’s add AWS provider to main.tf
as follows,
provider "aws" {
region = "us-east-1"
}
As seen in the above given example, Provider is a way to instruct Terraform to interact with AWS API. Every provider needs some kind of credentials to get authenticated with the API. In this example, we would provide AWS access key and secret key by adding those to the environment.
Export your AWS access and secret keys as environment variables.
export AWS_ACCESS_KEY_ID="Access-Key"
export AWS_SECRET_ACCESS_KEY="Secret-Key"
You could also add the above lines to your ~/.bashrc
, ~/.zshrc
files based on which shell you are using to avoid setting these credentials every time you open a new shell.
Now run the following command to initialize your environment.
terraform init
This should initialize the environment by
* Installing the provider from the registry as defined in main.tf
* Adding a lock file .terraform.lock.hcl
to record the provider details installed.
Launching a EC2 Instance with Terraform
To create anything with terraform, you need to write a Resource. You could get started by reading about using Resources hereResources Overview - Configuration Language | Terraform by HashiCorp
Resources are the building block in Terraform. These the actual cloud entities that you are either creating/deleting or modifying.
Following is a syntax for writing a simple resource. There are a few more properties such as meta-arguments, condition checks, timeouts etc. which we would explore later.
To get started, with the AWS provider, you would create EC2 instance by using aws_instance
as a resource. You could refer to this document Terraform Registry, to find the details about using this resource. You should be specially paying attention to the Argument References
Add the following code to same main.tf
file to define creation of a new EC2 instance by name frontend
.
resource "aws_instance" "frontend" {
ami = "ami-052efd3df9dad4825"
instance_type = "t2.micro"
tags = {
Name = "tf-frontend-01"
App = "devops-demo"
Maintainer = "Gourav Shah"
}
}
Validate terraform configurations with,
terraform validate
Optionally, you could further format the configuration file automatically to follow correct terraform style with,
terraform fmt
Now you could let Terraform create a Change Plan using the following command,
terraform plan
To apply this plan and build actual infrastructure, in this case create a ec2 instance with,
terraform apply
Terraform apply first generates a plan and then asks for approval as,
[Sample Output]
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
Say yes
to proceed, which will create the instance.
[Sample Output]
aws_instance.frontend: Creating...
aws_instance.frontend: Still creating... [10s elapsed]
aws_instance.frontend: Still creating... [20s elapsed]
aws_instance.frontend: Still creating... [30s elapsed]
aws_instance.frontend: Creation complete after 36s [id=i-04b03b981dbc55d7c]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Try running apply command again.
terraform apply
It does nothing this time since the instance was already created.
Check the state file created using the following or similar command specific to your OS,
cat terraform.tfstate
Modifying Properties of an Existing Instance
Begin by applying terraform once
terraform apply
Since there is no change from the previous run, it does not plan to change anything. The property of terraform where it refreshes the current state, compares it with the desired state, and decides to make change only if necessary is idempotence. This makes terraform safe to apply multiple times.
Lets try making a change by adding a property which is mutable. To do this, just add one more tag to the existing map of tags
as,
...
tags = {
Name = "tf-frontend-01"
App = "devops-demo"
Maintainer = "Gourav Shah"
Role = "frontend"
}
}
Where Role = "frontend"
is the new property.
[Sample Output]
# aws_instance.frontend will be updated in-place
~ resource "aws_instance" "frontend" {
id = "i-04b03b981dbc55d7c"
~ tags = {
+ "Role" = "frontend"
# (3 unchanged elements hidden)
}
~ tags_all = {
+ "Role" = "frontend"
# (3 unchanged elements hidden)
}
# (29 unchanged attributes hidden)
# (7 unchanged blocks hidden)
}
Since this property is mutable , it would update the instance in place, without needing for it to be recreate. Say yes to apply the changes.
Now, try updating a property which is immutable e.g. keypair. Update main.tf
by adding a key pair to be associated with this instance.
resource "aws_instance" "frontend" {
ami = "ami-052efd3df9dad4825"
instance_type = "t2.micro"
key_name = "demo"
tags = {
Name = "tf-frontend-01"
App = "devops-demo"
Maintainer = "Gourav Shah"
Role = "frontend"
}
}
Where, key_name = "demo"
is the new argument added. Ensure a key pair by name demo
is present on AWS for the region for which you are creating this instance.
terraform apply
This time terraform plan should show you that its going to recreate the instance, since the change is immutable.
# aws_instance.frontend must be replaced
-/+ resource "aws_instance" "frontend" {
~ arn = "arn:aws:ec2:us-east-1:665496447754:instance/i-0a477fc11e0055d72" -> (known after apply)
~ associate_public_ip_address = true -> (known after apply)
..
..
+ key_name = "demo" # forces replacement
..
..
Plan: 1 to add, 0 to change, 1 to destroy.
Do you want to perform these actions?
Say yes to have the instance be recreated, this time with a key pair.
[Sample Output]
aws_instance.frontend: Destroying... [id=i-0a477fc11e0055d72]
aws_instance.frontend: Still destroying... [id=i-0a477fc11e0055d72, 10s elapsed]
aws_instance.frontend: Still destroying... [id=i-0a477fc11e0055d72, 20s elapsed]
aws_instance.frontend: Still destroying... [id=i-0a477fc11e0055d72, 30s elapsed]
aws_instance.frontend: Destruction complete after 32s
aws_instance.frontend: Creating...
aws_instance.frontend: Still creating... [10s elapsed]
aws_instance.frontend: Still creating... [20s elapsed]
aws_instance.frontend: Still creating... [30s elapsed]
aws_instance.frontend: Creation complete after 37s [id=i-02b2a78e90ac9e89e]
Apply complete! Resources: 1 added, 0 changed, 1 destroyed.
If you check from the AWS Console, you shall now see a new instance created, this time associated with the keypair you defined e.g. demo
as shows in the image below.
Deleting Instance with Terraform Destroy
terraform destroy -auto-approve
[Sample Output]
Terraform will perform the following actions:
# aws_instance.frontend will be destroyed
- resource "aws_instance" "frontend" {
- ami = "ami-052efd3df9dad4825" -> null
..
...
Plan: 0 to add, 0 to change, 1 to destroy.
aws_instance.frontend: Destroying... [id=i-02b2a78e90ac9e89e]
aws_instance.frontend: Still destroying... [id=i-02b2a78e90ac9e89e, 10s elapsed]
aws_instance.frontend: Still destroying... [id=i-02b2a78e90ac9e89e, 20s elapsed]
aws_instance.frontend: Still destroying... [id=i-02b2a78e90ac9e89e, 30s elapsed]
aws_instance.frontend: Destruction complete after 32s
Destroy complete! Resources: 1 destroyed.
Summary
In this lab, you learnt how to get started with terraform to build your infrastructure. You configured terraform, added and initialised a provider, created a resource using HCL (Hashicorp Configuration Language), learnt to apply the configuration and also to modify and destroy a resource with terraform.