We are in part 3 of this series (part 1 , 2 ) and now the real fun with terraform begins.
In this blog, our main focus will be on Production level Best practices with terraform? But wait, we are still in 101 level and talking about production level? Yes, because even at the 101 level, this series tries to inculcate, and grow the sense of production-level practices right from the beginning.
Prerequisites
Provider Versioning
terraform {
required_providers {
github = {
source = "integrations/github"
version = "4.25.0-alpha"
}
}
}
- Terraform has 2 kinds of providers one which is managed by terraform and one which is managed by the provider itself. For terraform managed providers we don't need to write provider block for terraform managed providers like aws.
- But from terraform 0.13 and above, terraform recommends using provider block for all kinds of providers.
- terraform uses the provider's underlying plugins to interact with the provider and provision infrastructure. Let's say today the version of provider plugins is v1 and some new service came and provider included that service insider new plugin version v2.
- In this case, we need to upgrade our provider version too in order to use that service.
How does provider block work?
- From the official docs of aws provider this is how terraform recommends using AWS provider
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "4.18.0"
}
}
}
provider "aws" {
# Configuration options
}
- This piece of code will download plugins for version 4.18.0
terraform init
terraform init
- this command produces a lock file
.terraform.lock.hcl
which maintains the version of your provider plugins. - in order to upgrade to the latest version of plugins, we need to update the version in our lock file and use init command with
-upgrade
flag
terraform init -upgrade
Note:-
- if you don't use
-upgrade
flag lock file won't allow using the upgraded version even though you mentioned it in the code. - You many even see symbols like version = ~>3.0 or >=3.0 or <=3.0
- ~> simply means download provider plugin in 3.x range
- >= simply means download provider plugin ranging = or greater than 3.0 range
- **<= **simply means download provider plugin ranging = or lesser than 3.0 range
Production Importance
It is considered to use fix version for example version = "4.18.0"
, because there may be a chance when you use ~> that terraform installs the latest plugin version which has bugs and your production may break. So it is always advised to use the tested version and then upgrade to newer plugin versions.
Outputs and Attributes
- When we are provisioning infra, there is always a need to know the attributes of provisioned infra for example deployed ec2 IP address or EIP public IP.
Terraform allows doing this by writing output block for the particular resource.
code.tf file
provider "aws" {
region = "us-west-2"
access_key = "your-access-key"
secret_key = "your-secret-key"
}
resource "aws_instance" "myFirstEc2Instance" {
ami = "ami-0ca285d4c2cda3300"
instance_type = "t2.micro"
}
output "ec2_ip" {
value = aws_eip.lb.public_ip
}
- output block's has the format of output name followed by value = resource_name.local_resource_name.atrribute_name.
- attributes for each resource can be found in the attribute reference documentation.
output "ec2_ip" {
value = aws_eip.lb.public_ip
}
- the same format of
resource_name.local_resource_name.atrribute_name
can be used to cross-reference resources for example associating eip to ec2.
resource "aws_eip_association" "eip_assoc" {
instance_id = aws_instance.myFirstEc2Instance.id
allocation_id = aws_eip.lb.id
}
Variables
Why do we even need it?
- Just like any other programming language, when you want to use a single value, hardcoding it will be fine, but if the same value is being used in 10 other places then if in future that value changes; it needs to be updated in 10 other places too.
- So an ideal practice is to store value in a central place and then reference it in our code. If in future that value changes, just change in source.
How to store and reference variables?
There are 4 ways to store variables:-
- Default
- Command-line arguments
- file/custom file
- Environment
Default variables
- Going by the name, if command-line or environment or from a file, variables are sources, then default variables will be used.
- In order to use default variables, create a separate file
variables.tf
variable "instance_type" {
default = "t2.micro"
}
- In order to access a variable in our code,
var.name_of_variable
resource "aws_instance" "myFirstEc2Instance" {
ami = "ami-0ca285d4c2cda3300"
instance_type = var.instance_type
}
Note:- *If there is variable defined in variables.tf
, and there is no default value, then terraform will show a prompt at the time of terraform apply
*
variable "instance_type"{}
Command-line args
- Command-line arguments are used to add values for testing.
- To add command line arguments
-var="var_name=value"
terrform apply -var="instance_type=t2.large"
- Note:- If the default variable is present it will override by the command line argument.
From File
- Now default variables are used for default when no value is used, but usually, in production, there is always a value specified.
- In order to source variables from a file create a file called
terraform.tfvars
( naming convention is important)
instance_type = "t2.large"
Note :-
- to use a custom file name you need to use
-var-file
flag
terraform aply -var-file="custom_file.tfvars"
Environment variables
- There is another way to source variables i.e by using them as environment variables.
- syntax is as follows
export TF_VAR_instance_type="t2.nano"
Note:- if you are using environment variables then make sure there is no value present terraform.tfvars otherwise, the export variable value will be overwritten by the tfvars value.
Data type of variable
- Let's say there is a condition to create a iam user which by employee id, which only contains the number.
To add this constraint we need, data type defined as numbers inside
variables.tf
filevariables.tf file
variable "user_name" {
type = number
}
- code.tf file
provider "aws" {
region = "us-west-2"
access_key = "your-access-key"
secret_key = "your-secret-key"
}
resource "aws_iam_user" "user" {
name = var.user_name
}
- terraform.tfvars file
user_name = "jatin123"
- This will result in an error because
user_name
expects a number rather than a string.
- However, if we use a number that works.
user_name = "123"
- There are 5 kinds of data types we can use
string
,number
,bool
,list
, andmap
. reference
From a cloud engineer perspective
- In this article, we saw best practices when it comes to provider versioning.
- Different ways to input variables.
- How to type-safe our variables using data types.
Feel free to post your questions or suggestions in the comments, let's learn together.
Top comments (0)