Skip to content

Latest commit

 

History

History
641 lines (450 loc) · 37.1 KB

terraform_for_beginners.md

File metadata and controls

641 lines (450 loc) · 37.1 KB

Terraform for beginners

Color boxes used in this tutorial

In this tutorial I'm using colored boxes according to the following conventions:

Recap
Useful summaries and takeaways.

Answers from ChatGPT or GPT-4.

Digressions
Just random digressions and musings.

What is Terraform?

So, you might have heard about Terraform, but what exactly is Terraform?

Let's ask ChatGPT:

Terraform is an infrastructure as code software that allows you to provision and manage cloud, infrastructure, and network resources.

Hmm... I'm not sure what that means...

Let's look at another definition from https://kodekloud.com/playgrounds/playground-terraform:

What is Terraform?

Terraform is an infrastructure provisioning tool. It helps automate the process of creating and managing infrastructure using configuration files.

Terraform Workflow

To provision infrastructure in Terraform, we follow three main steps: write, plan & apply.

Writing: In the writing stage, we define the infrastructure we want in a Terraform configuration file.

Planning: After writing our Terraform configuration, we use the "terraform plan" command to preview the changes that will be made to our infrastructure. We use this step to verify that the changes are correct before applying them.

Applying: The final step in the process is to use the "terraform apply" command to apply the changes defined in the execution plan. This will create/change the infrastructure to match the desired state defined in the configuration file.

Terraform Composition

Terraform consists of two essential parts: the core & the providers.

Core: Terraform is powered by an engine called Terraform core. The core is the Terraform binary (program) that provides all the basic functionality.

Providers: Terraform uses providers (plugins) to interact with different infrastructure providers, such as AWS, Azure, or Google Cloud. The providers handle all the logic required to authenticate with the providers and make API requests on our behalf. They also handle timeouts and errors that may occur during resource management.

I think the best thing to do at this point is to try out an example. But first we're going to need to:

Install Terraform

Terraform is a tool that can be installed on your computer.

The installation should be straightforward. Here are the instructions for installing Terraform on Mac/Linux or Windows https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli#install-terraform.

The software we just installed is the Terraform core mentioned earlier.

Verify the installation

After the installation, type

% terraform -help

or

% terraform -version
Terraform v1.4.5
on darwin_amd64

Your version of Terraform is out of date! The latest version
is 1.4.6. You can update by downloading from https://www.terraform.io/downloads.html

at a command-prompt (note: % is my prompt).

In fact, I just noticed that the version that I installed not long ago (Terraform v1.4.5) is already out of date!

Is Terraform open source?

Yes. Terraform is open source and it's written in Go. The source code can be found at: https://github.com/hashicorp/terraform.

Recap
Terraform is an infrastructure as code software that allows you to provision and manage cloud, infrastructure, and network resources.

To install Terraform on your machine follow the instructions at: https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli#install-terraform

Terraform is written in Go and it's open source.

What is a provider?

Providers are a logical abstraction of an upstream API. They are responsible for understanding API interactions and exposing resources.

(quote from: https://registry.terraform.io/browse/providers)

What is an upstream API?

I'm not sure what is meant by "upstream API" and how a "backstream API" relates to it.

As far as I understand, backstream APIs or services depend upon upstream ones.

Here are some examples of upstream and downstream for APIs and in a general context

upstream downstream
database application using the database
back-end front-end
extracting raw materials processing of materials into a product

In a software system, an application relying on a database a is an example of downstream while the database offers the upstream service; a front-end is downstream to a back-end because it depends on the back-end; a production process involves collecting raw materials during the upstream stage and processing them into a finished product during the downstream stage.

In a river, downstream is the direction the river flows. Upstream is going in the opposite direction to the river flow.

Let's ask GPT4 (*)

Upstream APIs typically refer to the APIs that a particular software system or service depends on to function properly. In other words, an upstream API is like a supplier of data or functionality that another system relies on. For example, if you have an app that needs to retrieve weather data from a weather service API, then the weather service API is considered the upstream API.

On the other hand, downstream APIs refer to the APIs that are dependent on a particular system or service. A downstream API is like a consumer of data or functionality that another system provides. For example, if you have an app that provides a payment gateway for customers to purchase products, then the payment gateway API is considered the downstream API because it relies on your app to function.

In summary, upstream APIs are the ones that supply data or functionality to a system, while downstream APIs are the ones that consume data or functionality provided by a system.

(*)
I'm currently using the free trial of GPT-4 since my ChatGPT trial expired. I'm planning to get a license for one of the two after my current trial expires. I've found these language models very useful for my daily work—even though they are missing the "reasoning" part, they contain a great amount of knowledge (albeit sometimes not up-to-date) and they can often understand my information needs better than Google—at least that's my impression so far, very good at reformulating text, elaborating on a given topic, documenting code, debugging scripts, generate demos, ... but this would the topic of a longer discussion ...

The terraform Registry

Terraform providers are listed in the Terraform registry (https://registry.terraform.io/).

The first three providers listed in the Terraform registry when you click on "browse providers" are:

Provider # Downloads this month # Downloads overall
AWS 34.7M 1.7B
Azure 6.2M 330.5M
Google Cloud 9.2M 290.8M

I'm just going to add two other providers that interest me: Openstack because I'm working with it and local because we are going to use it for a demo later on.

Provider # Downloads this month # Downloads overall
OpenStack 388,655 11.4M
local 4.6M 198.1M

By clicking on "Documentation" for the AWS provider you get a description of what this provider has to offer:

AWS Provider Use the Amazon Web Services (AWS) provider to interact with the many resources supported by AWS. You must configure the provider with the proper credentials before you can use it.

Use the navigation to the left to read about the available resources.

To learn the basics of Terraform using this provider, follow the hands-on get started tutorials. Interact with AWS services, including Lambda, RDS, and IAM by following the AWS services tutorials.

Hashicorp's tutorials are great
Still, it might be convenient to finish the present tutorial first if you're a beginner at Terraform.

Recap
Terraform providers are abstractions of so-called upstream APIs. These are typically the APIs used to set up a computing infrastructure in the cloud, such as AWS, Azure, or Google Cloud.

Terraform providers are listed in the Terraform registry (https://registry.terraform.io/).

The Terraform registry also contains a trove of documentation, examples, tutorials, and interactive labs.

Try an online example

The best way to try out Terraform is to use the hands-on labs provided by Hashicorp. These are live environments where one can try things out while learning https://developer.hashicorp.com/tutorials/library?product=terraform&isInteractive=true.

Run online hands-on lab

https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli is a lab where you're guided throught the installation of an nginx Docker container.

If you don't know what Docker is you can check the tutorial Docker for beginners.

Click on "Show Terminal" and in the next screen on "Start". The setup takes a couple of minutes. Once the environment is setup, you can work with it 20 minutes before it expires.

In this environment you can choose between "Code Editor" and "Terminal". "Code Editor" is a simple text editor and "Terminal" is a Linux terminal where you can enter the commands suggested in the side panel.

A small digression
The lab environment is itself a Linux virtual machine with 2GB RAM and 20GB disk space. You can see this by running the following commands: The command systemd-detect-virt detects execution in a virtualized environment (in this case kvm).

The main.tf file

The first step in the tutorial consists in creating a main.tf file. You can copy-and-paste the text using the editor or else type this command in the terminal to populate the main.tf file:

cat >main.tf <<EOF
terraform {
  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = "~> 2.15.0"
    }
  }
}

provider "docker" {}

resource "docker_image" "nginx" {
  name         = "nginx:latest"
  keep_locally = false
}

resource "docker_container" "nginx" {
  image = docker_image.nginx.latest
  name  = "tutorial"
  ports {
    internal = 80
    external = 8000
  }
}
EOF
Another digression
The construct <<EOF is useful for creating files on the command-line. Note that you can use any other string in place of EOF (as long as it matches the final one). I often use LOL in place of EOF because why not?

See also: How does "cat << EOF" work in bash?

A .tf file is a plain text file containing Terraform code. Sometimes .tf files are also called configuration files (see https://developer.hashicorp.com/terraform/language/files).

Terraform code can be saved all in one file or spread across multiple .tf files for convenience in the case of larger projects.

Initialize the project

By running

terraform init

in the terminal you initialize the project. The initialization consists in downloading a plugin that allows Terraform to interact with Docker.

Run the project

With

terraform apply

you're going to start the infrastructure defined in the main.tf configuration file.

You'll be required to enter yes to confirm that you want to proceed. To skip this step, use

terraform apply -auto-approve

Is the project running?

Use the command

docker ps

to check that a Docker container is running. This is the container that has been started ("provisioned") by Terraform using Terraform's Docker provider.

Destroy the infrastructure

Use the command

terraform destroy

to stop the Docker container.

Recap
Terraform is a tool for running projects. A project is defined in one or more Terraform configuration files. These are text files ending with .tf that resemble a JSON files.

Terraform tutorials and learning environments can be found at https://developer.hashicorp.com/terraform/tutorials.

You can install Terraform on your machine or use Terraform's online learning environments to try out an initial demo.

The main commands for launching a project with Terraform are:

  • terraform init
  • terraform apply (or terraform apply -auto-approve)
  • terraform destroy

In Terraform lingo this is also called "provisioning an infrastructure".

A brief introduction to the Terraform language

Terraform configuration uses the Terraform language

which is a rich language designed to be relatively easy for humans to read and write. The constructs in the Terraform language can also be expressed in JSON syntax, which is harder for humans to read and edit but easier to generate and parse programmatically.

(quote from https://developer.hashicorp.com/terraform/language/syntax/configuration)

Arguments and blocks

The Terraform language syntax is built around two key syntax constructs: arguments and blocks ((see https://developer.hashicorp.com/terraform/language/syntax/configuration#identifiers).

An argument assigns a value to a particular name:

image_id = "abc123"

A block is a container for other content:

resource "aws_instance" "example" {
  ami = "abc123"

  network_interface {
    # ...
  }
}

A block has a type (resourcein this example). Each block type defines how many labels must follow the type keyword. The resource block type expects two labels, which are aws_instance and example in the example above. A particular block type may have any number of required labels, or it may require none as with the nested network_interface block type.

Following the block type keyword and any labels, the block body is delimited by the curly brackets ({}). Within the block body, further arguments and blocks may be nested, creating a hierarchy of blocks and their associated arguments.

The terraform block to begin with

The first block type that usually appears in a Terraform configuration file is the terraform block type. The terraform block type can be used to:

  • specifying which version of Terraform we want
  • what providers are required for running the project (required_providers block)

The Terraform block is often put into a separate file called terraform.tf as a way to separate settings into their own file.

Here's a sample terraform.tf file (from https://dev.to/af/hashicorp-configuration-language-hcl-blocks-5627:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 2.0"
    }
  }
  required_version = ">= 1.0.1"
}

Note that the terraform block type requires no labels, it is just: terraform { ... }. Same for the required_providers block type.

The line required_version = ">= 1.0.1" tells Terraform that we need at least version 1.0.1 for Terraform.

In the block required_providers we require the aws provider, specify where to find it (in source) and what is the minimal allowed version ("~> 2.0" means all versions that begin with 2—see the official documentation on how to specify version constraints).

Here can be found all Terraform providers with examples on how to use them: https://registry.terraform.io/browse/providers.

Identifiers

Argument names, block type names, and the names of most Terraform-specific constructs like resources, input variables, etc. are all identifiers.

Identifiers can contain letters, digits, underscores (_), and hyphens (-). The first character of an identifier must not be a digit, to avoid ambiguity with literal numbers. (https://developer.hashicorp.com/terraform/language/syntax/configuration#identifiers)

In the terraform block

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 2.0"
    }
  }
  required_version = ">= 1.0.1"
}

terraform, required_providers, aws, source, version, and required_version are all identifiers.

Honestly, I'm still not sure what's the difference between an identifier and a variable.

Comments

Useful to know: comments can be

  • single-lines beginning with a # or a //
  • multi-line between /* and */
On comment-lines starting with # or //
I find it nice to be able to choose between # and // for single-line comments, also because I once tried to use # for comments in Javascript—it was a Proxy Auto-Configuration file and it took me forever to find out why it wasn't working—in Terraform that wouldn't have been a mistake!

Does the order matter?

So, Terraform relies on a series of blocks and arguments that can be spread across different files. But does the order of these chunks of configuration within files matter? Does the naming of the files matter?

Up until now we've seen two different configuration files:

  • main.tf
  • terraform.tf

About ordering of files/blocks: according to the answer to "can we order .tf files in terraform?":

Terraform does not make any use of the order of .tf files or of the declarations in those files. Instead, Terraform decodes all of the blocks across all of your files and analyzes them to look for references between objects.

Also confirmed by "Multiple .tf files in a folder":

In Terraform 0.12+ (including 1.x), the load order of *.tf files is no longer specified. Behind the scenes Terraform reads all of the files in a directory and then determines a resource ordering that makes sense regardless of the order the files were actually read.

Btw
I find it a pity that a legitimate beginner's question like "can we order .tf files in terraform?" gets so many downvotes on Stack Overflow.

.tf files naming conventions

About file naming, I haven't found anything about rules for the names of .tf files, so I assume here it's all about conventions, best practices, common sense, ... just doing what everybody else does.

Go with the flow?
I'm not recommending it as a general principle. Let's hear what GPT-4 has to say.

Deciding whether to go with the flow or do what everyone else is doing is a personal choice that depends on your individual circumstances, values, and goals. Here are a few considerations to help you make your decision:

Individuality and Authenticity: Going with the flow may sometimes mean compromising your own unique perspectives, interests, or ambitions to align with the majority. If expressing your individuality and staying true to yourself is important to you, deviating from the norm might be a better choice.

Critical Thinking and Personal Growth: Following the crowd often involves accepting commonly held beliefs, practices, or trends without questioning them. If you value critical thinking and personal growth, it may be beneficial to challenge the status quo and explore alternative paths that align better with your own beliefs and aspirations.

Risk and Reward: Going against the grain can be riskier as it may involve uncertainty and resistance from others. However, it can also lead to greater rewards and opportunities. Consider the potential benefits and drawbacks of both options before making a decision.

Context and Consequences: Evaluate the specific situation and context in which you find yourself. Sometimes, going with the flow might be the most practical or efficient choice, especially if it aligns with your goals or if the deviation requires significant effort or resources.

Balance and Flexibility: It's not always an all-or-nothing decision. You can find a balance between going with the flow and asserting your individuality. Assess the situations where conformity is necessary and those where breaking away is beneficial. Flexibility allows you to adapt and make choices that serve you best in different circumstances.

Ultimately, the choice is yours, and there is no one-size-fits-all answer. Reflect on your values, aspirations, and what brings you fulfillment. Consider seeking guidance from trusted mentors or loved ones who understand your goals and can offer valuable insights.

Recap
Terraform files are composed of
  • arguments
  • blocks
An argument assigns a value to a given name, for instance image_id = "abc123".

A block is a container composed by a block type, zero or more labels and some content within curly brackets, like for instance terraform { ... }.

The terraform block type can contain requirements on the Terraform version needed as well as on the required providers.

Terraform configuration can be written to a single file or to multiple files. Ordering of configuration items does not matter as Terraform will take care of dependencies.

Comment lines start with # or //, multi-line comments are between /* and */.

Hello, World!

Let's build a "Hello, World!" project using the local provider.

This is the provider used to:

manage local resources, such as creating files

Everyone has access to a local provider—that's the filesystem you're currently working on—and there's no need for authentication.

From the docs: the local provider provides Resources for generating files (local_file and local_sensitive_file) and Data Sources for reading files (local_file and local_sensitive_file).

I'm not sure about the meaning of sensitive in Terraform, I believe it's about data such as passwords that should not be shown in plain text, but let us ignore that for now and just use local_file.

Open a terminal on your machine or start a terminal online (https://developer.hashicorp.com/terraform/tutorials/aws-get-started/infrastructure-as-code / Show Terminal / Launch --> / Start / Terminal) and type:

cat >main.tf <<LOL
terraform {
  required_providers {
    local = {
      source = "hashicorp/local"
      version = "2.4.0"
    }
  }
}

resource "local_file" "hello" {
  filename = "/root/learn-terraform-docker-container/hello"
  content  = "Hello, World!\n"
  file_permission = "0400"
}
LOL

followed by:

terraform init
terraform apply -auto-approve

These commands will create a file called hello whose content is the string "Hello, World!\n".

I'm beginning to like ...
... the succinct and intuitive Terraform syntax without semicolons or indentation.

Now type:

cat hello

If everything worked, you should get the output: Hello, World!. In this example the only thing you might need to change is filename in the terraform provider.

Let us introduce a variable named hello:

cat>main.tf <<EOF
variable "hello" {
  type = string
  description = "greeting"
  default = "Hello. World!"
}
output "hello" {
  value = var.hello
}
EOF

Now use terraform plan to see what Terraform is going to do when calling apply:

root@workstation:~/learn-terraform-docker-container# terraform plan
local_file.hello: Refreshing state... [id=60fde9c2310b0d4cad4dab8d126b04387efba289]

Terraform used the selected providers to generate the following execution plan. Resource actions are
indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

  # local_file.hello will be destroyed
  - resource "local_file" "hello" {
      - content              = <<-EOT
            Hello, World!
        EOT -> null
      - content_base64sha256 = "yYwktnfv9Ehgr+pvSTu67FuxxMuyCcb8K7tH9m/yrTE=" -> null
      - content_base64sha512 = "khYYvG2fgFlDfF4Dl7E/lzq3x6e4HwyjG3C/RI/YAKRgtn79oAIAiLyXv32dqXqeLOeyDUbgZkYuxEz2AoT5pw==" -> null
      - content_md5          = "bea8252ff4e80f41719ea13cdf007273" -> null
      - content_sha1         = "60fde9c2310b0d4cad4dab8d126b04387efba289" -> null
      - content_sha256       = "c98c24b677eff44860afea6f493bbaec5bb1c4cbb209c6fc2bbb47f66ff2ad31" -> null
      - content_sha512       = "921618bc6d9f8059437c5e0397b13f973ab7c7a7b81f0ca31b70bf448fd800a460b67efda0020088bc97bf7d9da97a9e2ce7b20d46e066462ec44cf60284f9a7" -> null
      - directory_permission = "0777" -> null
      - file_permission      = "0400" -> null
      - filename             = "/root/learn-terraform-docker-container/hello" -> null
      - id                   = "60fde9c2310b0d4cad4dab8d126b04387efba289" -> null
    }

Plan: 0 to add, 0 to change, 1 to destroy.

Changes to Outputs:
  + hello = "Hello. World!"

The terraform plan command lets you to preview the actions Terraform would take to modify your infrastructure, or save a speculative plan which you can apply later

(quote from https://developer.hashicorp.com/terraform/tutorials/cli/plan#create-a-plan)

Since all we are asking for in our Terraform configuration file is to create a variable (variable "hello" { } block), when running apply Terraform is going to destroy the existing file.

Now run apply -auto-approve

root@workstation:~/learn-terraform-docker-container# terraform apply -auto-approve
[ . . .]
local_file.hello: Destroying... [id=60fde9c2310b0d4cad4dab8d126b04387efba289]
local_file.hello: Destruction complete after 0s

Apply complete! Resources: 0 added, 0 changed, 1 destroyed.

Outputs:

hello = "Hello. World!"

Note that in this last example we did not use any provider but relied just on the Terraform core.

Recap

Terraform allows to manage any infrastructure in a structured and succint way.

Terraform plugins called providers let Terraform interact with cloud platforms and other services via their application programming interfaces (APIs).

Providers can be found in the Terraform Registry.