The Ops Community ⚙️

Cover image for Let's Terraform IT: Pt 3 -> Provider Versioning, Output, Attributes, Variables and Data types
Jatin Mehrotra
Jatin Mehrotra

Posted on

Let's Terraform IT: Pt 3 -> Provider Versioning, Output, Attributes, Variables and Data types

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"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode
  • 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 flow

  • 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?

terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "4.18.0"
    }
  }
}

provider "aws" {
  # Configuration options
}
Enter fullscreen mode Exit fullscreen mode
  • This piece of code will download plugins for version 4.18.0

terraform init

terraform init
Enter fullscreen mode Exit fullscreen mode
  • 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

lock file

terraform init -upgrade
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode
  • 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
}
Enter fullscreen mode Exit fullscreen mode

output

  • 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
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode
  • 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
}
Enter fullscreen mode Exit fullscreen mode

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"{}
Enter fullscreen mode Exit fullscreen mode

terraform variable prompt

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"
Enter fullscreen mode Exit fullscreen mode
  • 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"
Enter fullscreen mode Exit fullscreen mode

Note :-

  • to use a custom file name you need to use -var-file flag
terraform aply -var-file="custom_file.tfvars"
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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 file

  • variables.tf file

variable "user_name" {
  type = number
}
Enter fullscreen mode Exit fullscreen mode
  • 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
}
Enter fullscreen mode Exit fullscreen mode
  • terraform.tfvars file
user_name = "jatin123"
Enter fullscreen mode Exit fullscreen mode
  • This will result in an error because user_name expects a number rather than a string.

data type error

  • However, if we use a number that works.
user_name = "123"
Enter fullscreen mode Exit fullscreen mode
  • There are 5 kinds of data types we can use string, number, bool, list, and map. 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)