This post is just an overview about Terraform guided by the certification content. The material used in my study were:




Iac

Traditional infra issues:

  • Cannot guarantee infra config consistence
  • Lack of standardization
  • Slow provisioning
  • Human error
  • Difficulty in documentation

Infrastructure as code (IaC) is a mainstream pattern for managing infrastructure with configuration files rather than through a graphical user interface or through manual command line script sequences. It allows you to build, change, and manage your infrastructure in a safe, consistent, trackable, and repeatable way by defining resource configurations that you can version (in a version control system like GitHub), reuse, and share [1][2].

IaC makes it easy to provision and apply infrastructure configurations, saving time. It standardizes workflows across different infrastructure providers (e.g., VMware, AWS, Azure, GCP, etc.) by using a common syntax across all of them [1].

Benefits of IaC:

  • Relatively simple to learn and write
  • Makes Infrastructure more Reliable: changes become idempotent, consistent, repeatable, and predictable
  • Idempotent: if apply same configuration multiple times the results are always the same desired state.
  • Makes Infrastructure more Manageable
  • Reuse of code to deploy similar resources
  • Easily change and update existing infrastructure
  • Allows a user to turn a manual task into a simple, automated deployment
  • Allows recreate an application's infrastructure for disaster recovery scenarios
  • Allows infrastructure to be versioned
  • Creating a blueprint of your data center

Tools:

  • Infra provisioning tools: Terraform, AWS Cloudformation
  • Server templating tools: AWS Cloudformation, AWS EC2 Image Builder, Hashicorp Packer
  • Orchestration tools: Kubernetes, Docker Swarm and Apache Mesos
  • Configuration management tools: Puppet, Chef, Ansible, SaltStack and Microsoft DSC

Other References




Iac with Terraform

Terraform is a Iac tool used to manage infrastructure with declarative (desired end-state) configuration files. It manage multiple cloud platform, track resource changes using state, standardize the deployment workflow, manage dependencies between resources to create or destroy in the correct order, and prevent race conditions. Terraform create a dependency graph to ensure create the resources in the right order.

  • The state maps the resources configuration in the real world.
  • Terraform use APIs to handle the resources enabled by providers like AWS or Azure.
  • Core workflow: Write, Plan, Apply
  • It standarizes the deployment workflow allowing the same Terraform configuration to different providers

Benefits TF with IaC:

  • Store code in a version control helping the collaraboration work (GitHub.com, GitLab, BitBucket, Azure)
  • Easily integrate with application workflows (GitHub Actions, Azure DevOps, CI/CD tools)
  • Provide reusable modules for easy sharing and collaboration
  • Manage infrastructure on multiple cloud platforms (platform agnostic)
  • Human-readable configuration language
  • Terraform is written in HashiCorp Configuration Language (HCL)
  • Tests modifications using a "dry run" before applying any actual changes




Terraform Foundamentals

Terraform is immutable and declarative. It uses Hashicorp Configuration Language (HCL) as primary syntax, or optionally JSON, more used when importing existing infrastructure or when integrating Terraform with other tools that expect data in JSON format.

Terraform is available for macOS, FreeBSD, OpenBSD, Linux, Solaris, and Windows.

HCP has some recomendation that are not mandatory but are good practices.

Install

The terraform might be installed by the download of the binary, the package manager of your system like `brew`, or using a control version tool. There are two known tool for easily switch between terraform version: tfswitch and tfenv.

Workflow

The Terraform workflow is a 3 step process:

  1. Init - initialise the project:
    • Download any provider plugins
    • Initialise the working directory to recognise and manage the configuration files
    • Initialise any modules. TF downloads modules from the module registry or a version control system
    • It can automatically download community providers. It was added with Terraform 0.13. Before 0.13, terraform init would NOT download community providers
    • Set up the the backend for storing state files
  2. Plan - show the planned infrastructure changes:
    • Authentication credentials are used to connect to your infra
    • Read your configuration files and the current state
    • Compares them to determine what changes need to be made
    • Generates an execution plan allowing to review the changes before applying them
    • Terraform performs a refresh, unless explicitly disabled
    • Refresh-only mode: creates a plan whose goal is only to update the Terraform state and any root module output values to match changes made to remote objects outside of Terraform.
    • Save the plan using `-out` flag
  3. Apply - deploy the infrastructure:
    • Executes the planned changes modifying the infrastructure to match the desired state in your configuration files
    • Records the changes in the Terraform state file
    • Option for auto-approve as opposed to waiting for approval (necessary for CI/CD)
    • By default, uses parallelism (10 resources concurrently)
    • Replace: Using `terraform apply -replace=aws_instance.web` allows mark a specific resource for replacement without affecting other resources.
  4. Destroy - remove infra:
    • It needs an approval
    • Use the state file to destroy the resources

Debug: If you want to see details you can use variables TF_LOG and TF_LOG_PATH. It sends logs to stderr. Levels: TRACE (most verbose), DEBUG, INFO, WARN, ERROR [1][2]

Architecture

  • Terraform is a plugin-based architechture.
  • Terraform and plugins use Go language
  • Terrform Core: is the CLI. It is responsible for IaC, state management, dependency graph, plan, communication with plugins that happens via `remove procedure call` (RPC)
  • Terraform Plugins: exposes the implementation for the service, executed as a separate process, are responsible for initialization of libraries, authentication, define data and resources, define functions.

Resources

Terraform resources are:

  • Resources Blocks
  • Resources Behaviour
  • Meta-Arguments: depends_on, count, for_each, provider, lifecycle

Providers

  • The Terraform providers are responsible for allow the interaction between Terraform and cloud providers. That communication is done via APIs. Examples of providers: google, aws, azure, kubernetes, datadog, etc. [1][2][3][4][5][6]
  • Providers are distributed separately from Terraform itself, and each provider has its own release cadence and version numbers. [1]
  • The providers are plugins that are downloaded in `init` step considering the provider configuration.
  • The provider plugins are downloaded and stored in the `.terraform/providers` directory within the current working directory.
  • In case be necessary use multiple providers, terraform allows the use of alias to manage them.
  • The Vault Provider allows you to read and write to HashiCorp Vault from Terraform.
  • Good practice: specify the provider version inside the terraform setting block
  • Each Terraform module must declare which providers it requires, so that Terraform can install and use them. Provider requirements are declared in a required_providers block[1]
  • Authentication

The provider (plugin) can exist in local work directory without any reference to it in the Terraform configuration, and may not reflect a provider dependency. That provider dependency exisist if: exist any resource instace that belong to a provider in the state; exist any resource or data block in configuration; or explicit provider block in configuration.

In case multiple users sharing the same Terraform configuration then they should use the same version. For that, there are two alternatives to manage the provider version: provide version (required_version) in terraform block or dependency lock file. More details about the Terraform block you can see here.

For Dependency Lock approach:

  • The dependency lock is related to provider dependencies. It stores the version to be used.
  • The `init` step considers the version constraints in the configuration and the version selections recorded in the lock file as well. The Terraform will get the version recorded in lock file
  • This file (`.terraform.lock.hcl`) must be in root folder and pushed to the code repository
  • The lock file has the exact version of each provider is used (inside of required_providers block)
  • Use `terraform init -upgrade` to update the version
  • A checksum verification checks if the checksum for the packaged to be installed matches with the checksum related to the version in lock file

State

Concepts:[1][2]

  • All the metadata about deploy
  • Track what is deployed by a state file (mapping configuration to real-world resources)
  • State file is stored in plain text
  • Stored locally in `terrafome.tfstate` (default)
  • Stored remotely in AWS S3, Google Storage
    • Allows sharing state file with multiple teams
    • Remove Storage allows loking state so parallel execution don't coincide
    • Enables sharing output values with other Terraform configuration or code
  • State locking support remote state storage that prevents conflicts and potential corruption of the state file by multiple execution in deployment using the same state file simultaneously. [1][2]
  • It helps to create new deployment plans
  • It helps boost deployment performance by caching resource attributes for subsequent use, avoiding query the cloud provider's API repeatedly
  • Helps to determining the correct order to destroy resources

Commands:

  • State commands: list, show, output, graph [1]
  • Replace: rebuild a specific resources avoiding a full destroy operation. It recreates the resource even without modification in the configuration (previous versions: taint)(terraform taint RESOURCE_ADDRESS -> modifies state file (taints a resource, force to be destroyed and recreted))
  • Move: the `terraform state mv` moves resources from one state file to another. You can also rename resources with mv. The move command will update the resource in state, but not in your configuration file
  • Remove: Use a removed block to remove specific resources from your state. This does not destroy the infrastructure itself, instead it indicates that your Terraform configuration will no longer manage the resource.
  • Refresh: The terraform refresh command updates the state file when physical resources change outside of the Terraform workflow.
  • The import feature is used when a resource exists and you wish that be manage by terraform. [1][2][3]
  • Validate: terraform validate check and report errors within modules, attribute names, and value types to ensure they are syntactically valid and internally consistent.
  • terraform force-unlock or "terraform state rm" command followed by the "terraform state push"

Workspace

  • Definition: It is the state file working directory, where the `terrafome.tfstate` file will be stored.
  • Each Terraform workspace indeed uses its own state file to manage the infrastructure specific to that workspace
  • Isolate State:
    • workspace: path where the state is stored. It is not the best approach for isolate environments to avoid mistakes.
    • file layout: best solution for the full isolation between the environments: each TF configuration file must be inside a different folder; and must configure a different backend for each environment with different authentication and access controll (e.g, different S3)
  • A workspace can only be configured to a single VCS repo, however, multiple workspaces can use the same repo.[1]
  • Migration: to migrate a backend is necessary to move the state file and configuration to a new location. The backend can be migrate even if exists managed resources whithout de-provisioning anything
  • The default local backend stores the state file on the local filesystem of the machine where Terraform is being run. It uses system APIs to lock the state file and performs all operations locally.
  • Alternatives available to provide the remaining values to Terraform to initialize and communicate with the remote backend: use the -backend-config=PATH flag to specify a separate config file enables users to store sensitive information in a dedicated file that can be securely managed; directly querying HashiCorp Vault for the secrets; interactively on the command line
  • Commands: [1][2]
    		terrraform workspace new NAME
    		terraform workspace select NAME
    		terraform workspace list
    		Variable Access: ${terraform.workspace} 
    		

Variables and Outputs

Usually the map key/value of variables are inside the file `terraform.tfvars` and the declarations whith their data types, and names are inside `variables.tf`. Also, the variables can be environment variables that must be in the format TF_VAR_VARIABLENAME.

Type Constraints:

  • Primitive types: number, strong, bool
  • Complex types: Collection (list, map, set), Structural (tuple, set , object - multiple primitives)
  • Dynamic Type: any (not decided yet)

The locals block is another way to declare variables but it can create variables and use expressions.

The input variables are the values you pass to the terraform configuration. When they are declared inside the module is necessary to take care if it is sensitive or not because even sensitive variables are stored inside state file. If you don't want to store that, you can mark it as ephemeral.

As of Terraform 0.9, Terraform does not persist state to the local disk when remote state is in use, and some backends can be configured to encrypt the state data at rest.

If you pass that values using environment variable and it is undeclared, no warning or error will be shown. In case the values are passed inside of `.tfvars` file, then a warn will be shown, what helps to identify some mistakes. In case it is passed by command line then it will return an error.

The terraform precedence to load th variables is: Environment variable, `.tfvars`, `.tfvars.json`, `.auto.tfvars` or `.auto.tfvars.json`, -var or -var-file (using the order given in command line).

Considering that risk of expose sensitive data, Terraform recommends to ignore: `terraform.tfstate`, `terraform.tfvars`, .terraform directory , terraform.tfstate and terraform.tfstate.backup, .tfvars files, .tfplan files.

Even using the Terraform provider for Vault, the tight integration between Terraform and Vault does not automatically mask secrets in the state file. Developers need to implement secure practices to handle secrets effectively. An alternative is to use environment variables to store sensitive information or use Terraform's data sources to retrieve the information from the environment.

References

Data Sources

In Terraform, data blocks are used to retrieve data from external sources, such as APIs or databases, and make that data available to your Terraform configuration. With data blocks, you can use information from external sources to drive your infrastructure as code, making it more dynamic and flexible. [1]

A data source in Terraform enables the configuration to fetch data from an external source, such as an API or a cloud provider, to be used elsewhere in the Terraform configuration. This allows Terraform to dynamically retrieve information needed for resource provisioning.[2]

Meta-Arguments: depends_on, count, for_each, provider, lifecycle

depends_on

In TF we can identify two types of dependencies: [1]

  • The `depends_on` argument in Terraform creates an explicit dependency between resources. This means that Terraform will wait for the specified resource to be created or updated before proceeding with the dependent resource.
  • Terraform implicit dependencies refer to the dependencies between resources in a Terraform configuration but are not explicitly defined in the configuration. Terraform uses a graph to track these implicit dependencies and ensures that resources are created, updated, and deleted in the correct order.

count

The count argument replicates the given resource or module a specific number of times with an incrementing counter[1]

It's necessary to take care of it when manipulate a list because the content can change the position (in case remove any object).

for_each

In Terraform, when you use the for_each argument in a resource block, Terraform generates multiple instances of that resource, each with a unique address. The address of each instance is determined by the keys of the for_each map, and it is used to identify and manage each instance of the resource. [1]

Dynamic blocks

Repeatable nested blocks which typically represent separate objects that are related to (or embedded within) the containing object. Supported within: resource, data, provider and provisioner [1]




Advanced Concepts

Modules

Module is a collection of code. The main folder of your code where you run terraform commands is called root module. The modules that you call are called child module.

It's possible pass arguments to the modules. In case the result of the module needs to be used by who call it, then is necessary to use the output to access it. That output values are defined within the module.

A point to be carefull is the scenario where is necessary to refactor the module. The state can be impacted and they can marke destroy to create a new one. To avoid this, you can follow this Refactoring tutorial and manage the states and resources to preserve the existent resources.

You can create your own module or reuse from some private/public registry. Here is how to use these modules.

The modules can be in public or private registry. Private registry modules have source strings of the form HOSTNAME/NAMESPACE/NAME/PROVIDER. This is the same format as the public registry but with an added hostname prefix.

Benefits:

  • support versioning
  • automatically generated documentation
  • allow browsing version histories
  • show examples and READMEs

Requirements to publish in TF registry:

  • the module must live in public GH repo
  • the module must be named terraform-PROVIDER-NAME
  • the module mus follow a specific structure: provide readme and using convension of main, variables, outputs
  • the repo must use Git tags semantic versioning (x.y.z) for releases

Provisioners

  • Provisioners are used to execute scripts on a local or remote machine as part of resource creation or destruction [1]
    • local-exec: invokes a local executable after a resource is created
    • remote-exec: invoke scripts or run commands directly on the remote server
  • Terraform way of bootstrapping custom scripts, commands or actions
  • Creation-time and Destroy-time

Functions

  • Terraforms comes pre-packaged with functions[1]
  • User-defined functions are not allowed
  • Test using console: $terraform console. Command-line interface (CLI) tool that allows you to interactively evaluate expressions in Terraform. The terraform console command opens a REPL (Read-Eval-Print Loop) environment, where you can type Terraform expressions and see the results immediately. Console is used to test expressions, debugging configuration and understand TF behaviour.

Categories:

  • Numeric: abs, ceil, floor, max, min
  • String: concat, replace, split, join, tolower, toupper
  • Collection: element, keys, length, merge, sort, slice
  • Filesystem: file, filebase64, dirname

Other References




Terraform Cloud and Enterprise

  • HCP Enterprise
  • HCP Terraform can be managed from the CLI but requires an API token
  • Terraform Community (Free)/CLI store the local state for workspaces in the directory called terraform.tfstate.d/WORKSPACENAME
  • In HCP Terraform, you can switch between workspaces directly within the HCP Terraform web interface without the need to use the CLI. In HCP Terraform, each workspace represents a separate environment.
  • The Terraform CLI is primarily used to run Terraform commands locally on your development machine. When working with HCP Terraform, you typically interact with your workspaces through the web UI or by using HCP Terraform's API.
  • HCP Terraform always encrypts state at rest
  • Workspaces, managed with the terraform workspace command, isn't the same thing as HCP Terraform's workspaces. HCP Terraform workspaces act more like completely separate working directories. HCP Terraform offers additional features and capabilities for managing workspaces, such as remote state management, collaboration tools, and version control integration, which are not available in the Community version. CLI workspaces (Community) are just alternate state files.
  • Air Gap: network security measure employed to ensure that a secure computer network is physically isolated. Install terraform in a place where internet is unavailable
  • Remote Operations: After approving a merge request, HCP Terraform will run a speculative plan to show the potential changes that will be applied to the managed environment. This allows users to review and validate the changes against any applicable Sentinel policies before applying them.
  • Upgrade TF version in HCP: When you create a new workspace, HCP Terraform automatically selects the most recent version of Terraform available. If you migrate an existing project from the CLI to HCP Terraform, HCP Terraform configures the workspace to use the same version as the Terraform binary you used when migrating. HCP Terraform also provides the ability to upgrade your Terraform version in a controlled manner. This allows you to upgrade your Terraform version in a safe and predictable way, without affecting your existing infrastructure or state.
  • HCP Terraform agents are lightweight programs deployed within your target infrastructure environment. Their primary function is to receive Terraform plans from HCP Terraform, execute those plans locally, and apply the desired infrastructure changes. This allows you to manage private or on-premises infrastructure with HCP Terraform without opening your network to public ingress traffic.
  • Workspace [1][2]:
    • HCP Terraform manages infrastructure collections with a workspace whereas CLI manages collections of infrastructure resources with a persistent working directory. This distinction helps in organizing and separating different environments or configurations.
    • HCP Terraform maintains the state version and run history for each workspace
    • CLI workspaces are alternative state files in the same working directory
  • Scope level using variables in HCP Terraform: You can create a variable set by adding variables to the variable set and then applying a variable set scope so it can be used by multiple HCP Terraform workspaces. You can also apply the variable set globally, which will apply the variable set to all existing and future workspaces
    • Multiple workspaces using a variable set
    • All current and future workspaces in a project using a variable set
    • A specific Terraform run in a single workspace
  • Sentinel or OPA with HCP Terraform
    • Both tools allow you to define custom policies to evaluate and control Terraform configurations before they are applied. Both offer powerful capabilities to enforce custom policies on your Terraform configurations, providing an additional layer of security and governance. By leveraging these tools, you can prevent sensitive information or undesired strings from being present in your infrastructure code, reducing the risk of accidental misconfigurations and potential security vulnerabilities. [1][2]
    • Sentinel Policy: policy as code framework integrated with HashiCoprp Enterprise
    • Real-time feedback on potential security risks in Terraform configurations during the development process
    • Sentinel and OPA can enhance security by preventing unauthorized changes to your managed infrastructure
    • Organizations can enforce resource naming conventions or approved machine images for improved consistency and clarity
    • Sentinel and OPA enable automated policy checks to enforce compliance standards before applying changes to production environments