Terraform is an infrastructure as code (IaC) tool that lets a user define both cloud and on-prem resources in human-readable configuration files to version, reuse, and share. In other words, it allows users to define and provision infrastructure resources, such as virtual machines, storage accounts, and networks, in a declarative configuration language.
For example, on Amazon Web Services (AWS), it would be lengthy to use the console to manually build an infrastructure. Terraform makes it faster and easier by providing a consistent and reproducible way to define, provision, and manage resources on AWS through a programmatic approach.
The key components of Terraform include:
- Configuration files: These files define the desired state of your infrastructure, specifying the resources and their configurations.
- Providers: Terraform providers are plugins that interact with APIs of different cloud providers (such as AWS, Azure, Google Cloud Platform, etc.) or other services to manage resources.
- Resource types: Terraform supports a wide range of resource types, representing various infrastructure components like virtual machines, networks, databases etc.
- Execution plans: Terraform generates execution plans to show what actions it will take when you apply your configuration, giving you a preview of changes before they are implemented.
- State management: Terraform maintains a state file that keeps track of the current state of your infrastructure. This allows Terraform to understand the relationships between resources and manage updates efficiently.
The following tutorial aims to show how to create a basic infrastructure: we will provision an EC2 instance on AWS. EC2 instances are virtual machines running on AWS, and a common component of many infrastructure projects. EC2 instances can be configured with various CPU, memory, storage, and networking options to meet different workload requirements. EC2 is widely used for hosting websites, running applications, processing data etc.
Requirements. For this tutorial, it is assumed that:
- You have installed Terraform on your machine.
- You know a bit of AWS.
- You have installed AWS CLI.
- You already have an account with AWS*.
I used Studio Visual Code as source-code editor (with the official Terraform extension) and GitHub.
----------------------------------------------------------------------------------------------------------------------I created a directory on my pc (one per Terraform project is advisable). Inside, I created an empty file called "main.tf" (the name is not important, but the extension is!).
The first step is to identify a supported provider for Terraform, in my case AWS. Think of it as plugins: I want to make sure that Terraform downloads the needed code to communicate with the AWS API. Indeed, Terraform does not come with all the codes to connect to all APIs when you install it; it would not be functional since there are many providers.
Instructions for the AWS provider can be found here:
In the instructions, there is an example about the setup:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
# Configure the AWS Provider and define a Region (optional)
provider "aws" {
region = "eu-west-2"
}
I copy this and paste it in main.tf. So, I declared to Terraform what provider I need.
Now, the problem is that Terraform needs to connect to my AWS account to create, manage or destroy resources. There are many ways to do that.
In AWS, for the user I created ad hoc for this exercise, I can 'Create New Access Key'.
The new access key comes with an 'Access Key ID' and a 'Secret Access Key' that can be seen only once (you can save them on your machine though). I could now add them in the main.tf file to instruct Terraform to connect to my AWS account through that key, as shown below:
provider "aws" {
region = "us-west-2"
access_key = "my-access-key"
secret_key = "my-secret-key"
}
However, hardcoding the key in this way is risky because someone might discover it by looking at main.tf.
The alternative is using the AWS CLI (make sure to have it installed). *
*(There are more alternatives which are ignored in this post).
In my terminal, I type:
aws configure
I am asked for the AWS Access Key ID, AWS Secret Access Key, the Default region name (the one I used for my provider in main.tf), and the Default output format (JSON is fine).
This process populates a file in my home directory that can be accessed with cd .aws (I am using Git Bash). The credential file in the aws directory will be automatically used by Terraform to connect to my AWS account; this is safer than hardcoding the keys in plain sight in main.tf as suggested before.
----------------------------------------------------------------------------------------------------------------------
In the link provided above (this), there are instructions to create various resources in AWS. I searched for EC2 and found 'aws_instance' under Resource. The basic code is:
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
tags = {
Name = "HelloWorld"
}
}
This can be copy-pasted in main.tf, under what I already wrote before. With this, I am telling Terraform to provide a resource which is an AWS instance named "web" by me (Terraform will remember this name for this resource for future uses but AWS will ignore it).
data.aws_ami.ubuntu.id is the AMI for that instance and the type is t3.micro (better to use t2.micro since it is free in AWS). ami and instance_type are examples of config options (there could be more). Let’s ignore tags for now.
This is the code I have now in main.tf:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
# Configure the AWS Provider and define a Region (optional)
provider "aws" {
region = "eu-west-2"
}
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = "t2.micro"
}
That AMI has an 'id' at its end: it means that it comes from another piece of code (attribute) on main.tf (usually it is a combination of resource + provider + name + id but let’s ignore this for now). I insert a specific AMI found on AWS under Launch Instance: “ami-0e5f882be1900e43b” * (use quotations if you don’t use the id).
*(Make sure to take a free one in the same Region you are working in. You can use Ubuntu).
Final code in main.tf (remember to save it):
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
# Configure the AWS Provider and define a Region (optional)
provider "aws" {
region = "eu-west-2"
}
resource "aws_instance" "web" {
ami = “ami-0e5f882be1900e43b”
instance_type = "t2.micro"
}
I open the terminal and navigate where the project is. Then I type the command:
terraform init
This command initialises my working directory containing Terraform configuration files. In brief, it:
- Downloads Providers: It downloads the necessary provider plugins specified in my configuration files.
- Initialises Backend: My file is now stored locally on my machine. However, it can be stored remotely on Amazon S3, Azure Blob Storage, Terraform Cloud etc. to ease collaborations between developers.
- Installs Modules: in case these are used.
- Initializes Working Directory: It initializes the working directory for Terraform, creating necessary files and directories.
There is a well know problem that you might encounter at this stage:
'Error: Failed to query available provider packages
│ Could not retrieve the list of available versions for provider hashicorp/aws: could not connect to registry.terraform.io: failed to request
│ discovery document: Get https://registry.terraform.io/.well-known/terraform.json'
If I curl https://registry.terraform.io/.well-known/terraform.json, I receive 'curl: (35) Recv failure: Connection was reset.'
To solve this, I simply downloaded a free VPN and did the terraform init while logged into the VPN.
If the command is executed correctly (green output in the terminal), you will find a series of new files in the working directory. One of these is the "terraform.tfstate" which is fundamental to Terraform's operation, providing a snapshot of the infrastructure's desired state and serving as the basis for making changes and maintaining infrastructure. Proper management and protection of the state file are crucial for effective Terraform workflows.
Next command to type in the terminal after terraform init:
terraform plan
This command compares what has been built already with the plan I coded in Terraform. It basically allows me to review proposed changes and ensure they align with my intentions before making them live. The green plus sign stands for “creation” and the red minus sign stands for “destruction”. Every resource is evaluated in comparison to the current state.
Since nothing was created before for my AWS user, terraform plan informs me that one resource only (the EC2 instance) will be added ('Plan: 1 to add, 0 to change, 0 to destroy.')
Now I can terraform apply to launch the instance ("yes" to approve the action when prompted). If I go into my AWS account under EC2 I can verify that the t2.micro instance with AMI ami-0e5f882be1900e43b is indeed running.
terraform destroy to destroy it (remember to do it to avoid costs of running instances on AWS).
No comments:
Post a Comment