A 100,000 Feet View of Terraform Basics

Christian Talavera
6 min readMay 24, 2021

Using ‘terraform init’

To begin working with Terraform and create IaC, a working directory on a host machine has to be chosen.

This is done with the following command:

terraform init

This command ‘initializes’ the working directory to be used with Terraform to create IaC code. It is a critical command that must be run before the start of any Terraform project.

Along with setting the Terraform working directory, it also does two other actions:

  • Downloads ancillary components — any required modules and plugin files that are needed for Terraform to run will be downloaded into the working directory
  • Sets up backend — the backend of Terraform is set up to store the ‘state file’, which contains a database of all the resources and configuration needed in the target environment

The files needed to perform these actions are either fetched from the public Terraform repositories, or a custom location defined by the administrator.

Terraform caches these required modules for later use. If a new version is available, Terraform will download new updates by default; this behavior can be modified with built-in arguments for the terraform init command.

The Terraform Workflow

It is best practice to follow the ‘Core Terraform Workflow’ procedures to use Terraform efficiently.

The workflow consists of Write → Plan → Apply

Write

This step involves the initial writing of the actual code to provision the needed resources.

This is usually stared off by hosting the Terraform code in a collaborative space. Hosting the IaC code in a location like GitHub allows for change management and version control, which allows for increased team collaboration on projects.

Plan

After writing the code, the changes that the code will make to the environment need to be reviewed to be sure that they are okay to apply; infrastructure edits are continually reviewed and added to be sure intended changes make it to the production environment.

If need be, additional code can be written to get the IaC to match what is needed.

Apply

Once review of the written code is complete, the resources outlined in the Terraform code can be physically deployed.

Writing out the code and reviewing for changes allows for the best practice of provisioning resources, as physically deploying first and making changes later can be time and resource consuming.

Key Terraform commands

The reason why using the Terraform workflow “Write → Plan → Execute” as best practice is because each step can be accomplished with it’s own command.

After the ‘writing’ stage, doing review of any written terraform projects during the ‘plan’ stage can be done with the terraform plan command; physically applying the changes during the 'execute' stage can be done with the terraform apply command. Afterwards, any resources can be deleted all at once with the terraform destroy command.

The ‘terraform plan' command

This command does a review of the written IaC code. The command reads the code and shows the ‘plan’ of how the code will deploy the specified resources. No actual resources are deployed at this point, changes to infrastructure outlined at this stage can be considered ‘read-only’.

Terraform authenticates with the desired cloud platform at this stage; the IT teams are able to review the outline for any needed changes before the plan is executed.

The ‘terraform apply' command

Once all needed edits and changes are done and finalized, the actual infrastructure can be deployed using this command. At this stage, the code’s statements and instructions are executed, and the infrastructure is deployed.

When this is done, the “state file” is updated with the created IaC code. This state file contains all the resources and their details that will be applied to the actual infrastructure; this state file can be stored locally, or in a remote location. This state file’s name is terraform.tfstate by default. All subsequent commands will always reference the state file.

The ‘terraform destroy' command

This command examines the stored state file, and destroys all the resources being tracked in the database. This allows for cleaning up and deleting of fleets of Terraform managed infrastructure without the use of complex scripts.

This is a non-reversible command and should be used with caution; backups should be made of the state file in case of accidental use of the terraform destroy command.

Reading Written Terraform code

By default, Terraform automatically executes any written code in files containing the .tf extension.

Initially, Terraform searches for the ‘providers’ that will be referenced in Terraform code using the built-in “Terraform providers registry”; custom providers can be used instead if needed.

Configuring the Provider

In order to interact with different cloud providers, Terraform has to download different plugins to be able to abstract away all of the API calls. The chosen platform is specified in the Terraform code as a ‘provider’; every different platform has its own provider.

provider is a reserved keyword in HCL and is used to express defining the cloud provider being used. The provided name is placed in double quotes "" and the configuration parameters are placed in curly brackets {}. Configuration parameters are all of the arguments that need to be specified in the cloud environment that is being deployed.

In the below example, the region being used in AWS is defined by the region argument:

provider "aws" {
region = "us-east-1"
}
provider "aws" {
region = "us-east-1"
}

Other arguments are specified, often times varying based on which cloud provider is being used.

in the below example using a Google Cloud environment, the authentication credentials are defined with the credentials argument , using the built-in function file() to fetch the file containing the credentials to login to the environment.

The name of the ‘project’ can be defined with the project argument; this argument is specific to Google Cloud.

provider "google" {
credentials = file("credentials.json")
project = "my-gcp-project"
region = "us-west-1"
}
provider "google" {
credentials = file("credentials.json")
project = "my-gcp-project"
region = "us-west-1"
}

Resource Block

A cloud resource that needs to be deployed can be specified in the Terraform code with ‘resource blocks’.

This is done using the resource argument, followed by the name of the type of resource that Terraform will provide, in this case its named "aws_instance" because an AWS EC2 instance will be created. Lastly, a user-created name to identify the resource block is defined; in this example, the name chosen is "web".

Within the curly brackets {} of the resource block, the image type for the instance must be defined with the ami argument; the actual type and size of the instance is defined with the instance_type argument. Each resource type has its own specific unique arguments.

resource " aws_instance" "web" {
ami = "ami-a1b2c3d4"
instance_type = "t2.micro"
}
resource " aws_instance" "web" {
ami = "ami-a1b2c3d4"
instance_type = "t2.micro"
}

To reference the created ‘resource block’ later in the Terraform code, the “resource address” can be used, using the resource type and the given name of the created resource.

The above created object would have a resource address of :

aws_instance.webaws_instance.web

The resource address notation allows for access of any attributes that this created object provides, which can referenced later in the Terraform code if needed.

Data Source

Along with creating resources for deployment, already existing cloud resources can be accessed as a ‘data source’ for the Terraform code; this follows a similar notation as a ‘resource block’, using data as the keyword, with the type of resource and user created name of the created data source specified.

Within the curly brackets {} would be the 'data source argument'. The identifying name of the resource being fetched is defined; in the below case specifying an AWS EC2 instance, an instance_id argument is used.

data "aws_instance" "my-vm" {
instance_id = "i-1234567890abcdef0"
}
data "aws_instance" "my-vm" {
instance_id = "i-1234567890abcdef0"
}

Data sources also have resource addresses, which can be referenced later in the code. The format for data sources would be as follows:

data.aws_instance.my-vm

--

--

Christian Talavera

DevOps Engineer writing about breaking into the industry