Terraform Certification Study Guide

Dec. 15, 2023, 11:22 p.m.

The following is a very long, but hopefully usefull, breakdown of Terraform's certification exam ojectives.

Terraform Exam Objetives

1 Understand infrastructure as code (IaC) concepts

1a Explain what IaC is

IaC is a way of managing infrastructure through configuration files rather than through a grahical user interface (GUI)

1b Describe advantages of IaC patterns

You can use version control on your IaC meaning you allows have a record of the changes made. You can also set up Terraform with a version control system allowing it to automatically make infrastructure changes when you commit your code.

2 Understand the purpose of Terraform (vs other IaC)

2a Explain multi-cloud and provider-agnostic benefits

You have the ability to use terraform to create your infrastructure on any cloud platform.

2b Explain the benefits of state

Terraform keeps track of your infrastructure in a state file, which acts as a source of truth for your system. Terraform compares your configuration file with the state file to determine what changes need to take place. You can share your state with teammates providing a stable cloud environment for Terraform to run in.

State is used to compare your terraform configuration with the real world infrastructure. State also ensures that remote objects are tied one to one with terraform resources in order to avoid situations in which there is ambiguity.

State is also used to track resource metadata for example the dependencies between resources.

Another benefit of state is performance: terraform stores a cache of attribute values for all resources in the state. This is used when querying providers for information about resources is too costly in terms of performance.

State can also be used for syncing across teams, so that everyone is working off the same version of the truth.

3 Understand Terraform basics

3a Install and version Terraform providers

Terraform Cloud and Terraform enterprize install providers as part of every run Terraform CLI finds and installs providers when initializing a working directory if a lock file is present terraform will obey it when installing providers.

3b Describe plugin-based architecture

plugin-based architecture allows developers to extend terraform architecture by writing new plugins or edit- ing old ones. Terraform is split into two parts Terraform Core and Terraform plugins
Terraform core which is responisble for reading and interpolating config files and managing state communi- cates with terraform plugins which are each implementations for a specific service eg AWS or GCP or an implementation for a provisioner eg bash. Provider Plugins * initialize any included libraries to make API calls * Authenticate with infrastructure provider * Define Resources that map to specific services

Provisioner Plugins * execute commands or scripts on the designated resource after creation or destruction

3c Write Terraform configuration using multiple providers

Provider configurations belong in the root module of a Terraform configuration. A provider is configured using a provider block:

provider "google" {
    project = "acme-app"
    region  = "us-central1"

}
"google" is the local name for this provier. The text inbetween the {} is the body which provides configu-

ration arguments for the provider.

You can define multiple configurations for the same provider, and select which one to use on a per-resource and per-module basis. This is mainly used to support different configurations for different regions.

in order to create multiple configurations for a given provider create multiple provider blocks within the same provider name. For each non default configuration provide the alias argument to provide an extra name segment. eg:

# The default provider configuration; resources that begin with ‘aws_‘ will use
# it as the default, and it can be referenced as ‘aws‘.
provider "aws" {
    region = "us-east-1"
}
# Additional provider configuration for west coast region; resources can
# reference this as ‘aws.west‘.
provider "aws" {
    alias  = "west"
    region = "us-west-2"
}

To recieve a configuration alias in a module from the parent module use the configuration_aliases argument in the required_providers as seen below:

 

terraform {
    required_providers {
    mycloud = {
        source  = "mycorp/mycloud"
        version = "~> 1.0"
        configuration_aliases = [ mycloud.alternate ]

}

} }

A provider block without an alias is the default provider. Resources that don’t set the provider will use the default provider configuration that matches the first word in the of the resource type name eg “aws” in “aws_instance”.

When terraform needs the name of a provider configuration it expects the name in the form <PROVIDER_NAME>.<ALIAS> using the example above it would be aws.west.

To select alternate provider configurations for a child module use it’s providers meta argument provider configuration should be mapped to which local provider names within the module. eg:

module "aws_vpc" {
    source = "./aws_vpc"
    providers = {
    aws = aws.west
    }

}

3d Describe how Terraform finds and fetches providers

Terraform can automatically download providers from a terraform registry or load them from a local mirror or cache
Terraform downloads providers from the terraform registry by default
for earch provider the source attribute defines an optional hostname, a namespave, and the provider type.

terraform {
    required_providers {
    aws = {
        source  = "hashicorp/aws"
        version = "~> 4.16"
    }

}

    required_version = ">= 1.2.0"
}
provider "aws" {
    region  = "us-west-2"

}

resource "aws_instance" "app_server" {
    ### ami           = "ami-830c94e3"
    instance_type = "t2.micro"

tags = {

    Name = "ExampleAppServerInstance"

} }

In the above example the provider’s source is defined as hasicorp/aws which is shorthand for reg- istry.terraform.io/hashicorp/aws.

Terraform will record all versions in the terraform lock file located in the working directory. The lock file is used to record the decisions terraform made about dependencies so it can remember them again upon running terraform init in the future. If a particular dependency has a version recorded in the lock file terraform will always use that version unless you run terraform init --upgrade; if no version is found terraform will download the newest version.

4 Use Terraform outside of core workflow

4a Describe when to use terraform import to import existing infrastructure into your Terraform state

terraform import [options] ADDRESS ID

terraform import will find the existing resources from ID and import it to your terraform state at the give ADRESS. You use terraform import when there is infrastructure that already exists that you want to be part of your state which is a record of what is being managed by terraform.

To import a resource first write a resource block for it in your configuration. You need to establish a name by which the resource will be known in terraform.

Then you run the terraform import command to link the existing infrastructure with the resource. 4b Use terraform state to view Terraform state

Inmostcasesyouwillusetheterraform statecommandtomodifythestateofyourterraformconfiguration The state file keeps track of changes made to resources in your configuration and maps them on to real world

instances.

use the CLI to interact with state using terraform show to print the statefile

terraform state list to get the resource names and local identifiers.

terraform state mv moves resources from one file to another. Moving resources is usfull when you want to combine modules or resources from other states, but do not want to recreate the infrastructure.

NOTE: unless you also update your config to include the moved instance it will be destroyed the next time you run terraform apply–because it is not defined in the configuration.

terraform refresh updates the state file when changes have happened to the infrastructure outside of the terraform workflow.

4c Describe when to enable verbose logging and what the outcome/value is

You enable verbose logging when you expect you have found a bug in terraform core and want to provide the logs to the dev team at terraform.

You enable logging by setting the TF_LOG environment variable to one of the following verbose levels: TRACE, DEBUG, INFO, WARN, or ERROR. You set the TF_LOG_PATH variable to append the logs to a specific file.

5 Interact with Terraform modules

5a Contrast and use different module source options including the public Terraform Module Registry

A Terraform module is a set of Terraform configuration files in a single directory. Even a simple configuration consisting of a single directory with one or more .tf files is a module. When you run Terraform commands directly from such a directory, it is considered the root module. So in this sense, every Terraform configuration is part of a module.

you can access modules from the terraform registry using the format: <NAMESPACE>/<NAME>/<PROVIDER> Or from a private registry using the the format: <HOST_NAME>/<NAMESPACE>/<NAME>/<PROVIDER>

5b Interact with module inputs and outputs

Input variables allow you to customize aspects of Terraform modules without altering the source code. This allows you to share modules accross different terraform configurations.

You could compare terraform modules to function definitions from a traditional programming language:

• input variables are like function arguments
• output values are like return values
• local values are like a functions temporary local values

input variables must be declared using a variable block:

variable "image_id" {
  type = string

}

variable "availability_zone_names" {
  type    = list(string)
  default = ["us-west-1a"]

}

variable "docker_ports" {
  type = list(object({
    internal = number
    external = number
    protocol = string
  }))
  default = [
    {
      internal = 8300
      external = 8300
      protocol = "tcp"

} ]

}

Output values output values make information about your configuration available on the command line, and expose information for other terraform configurations to use.

• a child module could use outputs to expose a subset of it’s resources to a parent module.

• a root module could use outputs to print certain values to the CLI after running terraform apply.

• when using remote state root terraform configurations can be accessed by other terraform configura- tions.

example output block:

output "instance_ip_addr" {
  value = aws_instance.server.private_ip

}

In a parent module, outputs of child modules are available in expressions as module.<MODULE NAME>.<OUTPUT NAME>

you can assign input variables via a terraform.tfvars file, the -var option when running terraform apply or terraform plan or through enviornment variables prepended with TF_VAR

5c Describe variable scope within modules/child modules

A module can call other modules, which lets you include the child module’s resources into the configuration in a concise way.

To call a module means to include the contents of that module into the configuration with specific values for its input variables. Modules are called from within other modules using module blocks:

module "servers" {
  source = "./app-cluster"

servers = 5 }

A module that includes a module block like this is the calling module of the child module.

The resources defined in a module are encapsulated, so the calling module cannot access their attributes directly. However, the child module can declare output values to selectively export certain values to be accessed by the calling module. eg:

resource "aws_elb" "example" {
  # ...
  instances = module.servers.instance_ids
}

5d Set module version

Use the version argument in the module block to specify versions eg:

module "consul" {
  source  = "hashicorp/consul/aws"
  version = "0.0.5"

servers = 3 }

If no version is set terraform will install the latest version. 6

6 Use the core Terraform workflow

6a Describe Terraform workflow ( Write -> Plan -> Create )

1. Write: Author infrasructure as code
2. Plan: Preview changes before applying
3. Apply: Provision reproducible infrastructure

After applying it is common to push your version control repo to a remote repo for safekeeping. The core workflow is a loop; the next time you want to make changes you start the process from the beggining.

6b Initialize a Terraform working directory (terraform init)

Initializes a working directory containing configuration files. This is the first command that should be run when starting a new config or cloning one from a remote source. Itis safe to run multiple times.

when you initialize a terraform workspace terraform configures the backend, installs all the providers and modules reffered to in your configuration, and creates a version lockfile if one does not already exist. You can also use terraform init to update your providers and modules as well as change your backend. Optional arguments:

-upgrade opt to upgrade plugins and modules as part of thier respective installation steps.

By default init assumes that the working directory contains a configuration and will try to initialize that first.

terraform uses the .terraform directory to store the project’s providers and modules. Use terraform init when:

• you create a new terraform configuration
• you clone a version controlled repo containing a terraform configuration
• you add remove or change the version of a module or provider
• you add remove or change the backend or cloud blocks within the terraform block of the workspace

6c Validate a Terraform configuration (terraform validate)

validate validates the config files in a directory. It does not access any remotes or APIs.

To verify the configuration in the context of a particular run (target workspace, input vars etc) use terraform plan instead.

6d Generate and review an execution plan for Terraform (terraform plan)

plan creates an execution plan which terraform will use to create your infrastructure.

When you use plan terraform reads the current state of any already existing remote objects to make sure that the terraform state is up to date. It compares the current configuration to the prior state and notes any differences. Finally it proposes a set of change actions which if applied make the remote objects match the configuration.

When running terraform in automation there is a way to save the plan to a file using terraform plan -out=FILE this file can then be passed to terraform apply as an extra argument.

6e Execute changes to infrastructure with Terraform (terraform apply)

the terraform apply command executes the actions proposed by the terraform plan. You can run apply on it’s own, in which case it will create an execution plan on the spot based on the statefile and the configuration, or you can pass apply a plan you had created previously with terraform plan -out=FILE_NAME. The second option is more for when you automate terraform.

Options:

lock-timeout=DURATION instructs terraform to retry applying a lock for a predefined duration.

Errors in terraform apply will print during the process and the process will be stopped; any resources created up until that point will no be rolled back; in order to reset the configuration you will need to submit a valid configuration to apply.

Common reasons for errors:

1. A change to a resource outsife of terraform’s control
2. networking or other transient errors.
3. An expected error from the upstream API eg duplicate resource name or reaching a usage limit. 4. An unexpected error from the upstream API eg an internal server error
5. A bug in the terraform provider code or terraform itself.

Usually you will plan and apply on an entire configuration; however, there are some situations when you will need to target specific resources. Terraform gives you two options for interacting with specific resources: -replace and -target.

use -replace when a resource has become unhealthy for reasons outside of terraform’s control and you want to replace it with the same configuration. Eg an error in the OS of a virtual machine requires it be replaced.

Use -target when you are troubleshooting an error that prevents terraform from applying your entire configuration at once. This may occur if a provider or API error leaves your resources in an invalid state that terraform cannot resolve. -target can be used to target individual resources specifically.

6f Destroy Terraform managed infrastructure (terraform destroy)

The terraform destroy command is a convenient way to destroy all remote objects managed by a terraform configuration.

destroy accepts all the options that apply accepts.

6g Apply formatting and style adjustments to a configuration (terraform fmt)

terraform fmt is used to apply the cannonical formating standards to the configuration files in a working directory.

7 Implement and maintain state

7a Describe default local backend

A backend in terraform defines where terraform’s state snapshots are stored.
A terraform can either specify a backend, for example terraform cloud, or default to storing state locally.

The local backend stores state on the local filesystem, locks that state using system APIs, and performs operations locally.

example configuration:

terraform {
  backend "local" {
    path = "relative/path/to/terraform.tfstate"
  }

}

7b Describe state locking

state locking prevents others from writing to the statefile while you are performing operations that update the statefile.

7c Handle backend and cloud integration authentication methods

terraform logincanbeusedtoautomaticallyobtainandsaveanAPItokenforTerraformCloud,Terraform Enterprise, or any other host that offers Terraform services.
Usage: terraform login [hostname]

7d Differentiate remote state back end options

Storing state remotely and accessing it via credentials is the prefered way to work with terraform on a distributed team.

7e Manage resource drift and Terraform state

Resource drift is when your terraform state file does not exactly match your real world infrastructure. It can happen when changes are made outside of terraform.

Refresh only mode instructs terraform to create a plan which updates the terraform state to match changes to remote objects outside of terraform. This is usfull if state drift has occurred.

terraform plan -refresh-only or terraform apply -refresh-only
plan allows you to see the proposed updates to the state file and apply makes the changes to the state file.

Note: terraform plan and apply will automatically update the state file, but they may be also used to create infrastructure. Refresh only is used when you only want to refresh state.

terraform import is used to bring resources created outside the terraform workflow under terraform manage- ment. The format for terraform import is: terraform import <resource definition> <id of remote object>

7f Describe backend block and cloud integration in configuration

terraform configures the backend through a backend block. If you are setting up a terraform cloud to manage your state remotely you will use a cloud block. Cloud and backend blocks preclude the use of one another. eg backend block:

terraform {
  backend "remote" {
    organization = "example_corp"
    workspaces {
      name = "my-app-prod"

} }

}
note: you must always run terraform init after making a change to the backend.

You can use a file to store configuration information, or you can pass configuration information through the command line. eg

terraform init \
    -backend-config="address=demo.consul.io" \
    -backend-config="path=example_app/terraform_state" \
    -backend-config="scheme=https"

the file itself holds the contents of the backend block as top level attributes. eg:

address = "demo.consul.io"
path    = "example_app/terraform_state"
scheme  = "https"

Terraform Cloud example cloud block:

terraform {
  cloud {
    organization = "example_corp"
    ## Required for Terraform Enterprise; Defaults to app.terraform.io for Terraform Cloud
    hostname = "app.terraform.io"
    workspaces {
      tags = ["app"]

} }

}

7g Understand secret management in state files

statefiles always store secrets in plain text. Hashicorp Vault and Terraform cloud are options for not storing state locally so that the secrets are maintained.

8 Read, generate, and modify configuration

8a Demonstrate use of variables and outputs

Variables can be defined in three ways:

1. in a file called terraform.tfvars
2. input at the command line with the flag -var
3. assigned as environment variables prepended with TF_VAR

outputs are defined in a file called outputs.tf. The outputs can be passed to other modules/configurations to be used as inputs therein.

See section 5b for more info.

8b Describe secure secret injection best practice

best practice is to use hashicorp’s vault provider to generate short lived and appropriately scoped AWS credentials.

You can mark certain attributes of modules senesitive so they will be redacted when they are printed in terraform output; the statefile will still contain the sensitive information howver.

avoid placing secrets in your Terraform config or state file wherever possible, and if placed there, you take steps to reduce and manage your risk.

Vault can be used to store your secrets and use them in your configuration or handle connections to cloud providers.

8c Understand the use of collection and structural types

A complex type is a type that groups multiple values into a single value. There are two categories of complex types:

1. collection types - for grouping values of the same type 2. structural types - for grouping values of different types.

Collection types:

list(. . . ): a sequence of values identified by consecutive whole numbers starting with zero.

map(. . . ): a collection of values where each is identified by a string label.

set(. . . ): a collection of unique values that do not have any secondary identifiers or ordering.

Structural types:

object(. . . ): a collection of named attributes that each have their own type.

tuple(...): a sequence of elements identified by consecutive whole numbers starting with zero, where each element has its own type.

NOTE: Structural types require a schema as an argument, to specify which types are allowed for which elements.

8d Create and differentiate resource and data configuration

A data resource is a special kind of resource which is defined in a data block eg:

data "aws_ami" "example" {
  most_recent = true
  owners = ["self"]
  tags = {
    Name   = "app-server"
    Tested = "true"
  }

}

A data block requests that Terraform read from a given data source (“aws_ami”) and export the result under the given local name (“example”)

each data instance will export one or more attributes which can be used in other resources as reference expressions. eg:

resource "aws_instance" "web" {
  ami           = data.aws_ami.web.id
  instance_type = "t1.micro"

}

8e Use resource addressing and resource parameters to connect resources together

A resource address is made up of two parts:

[module path][resource spec]
The module path in turn takes the form: module.module_name[module index]
A resource spec has the form: resource_type.resource_name[instance index] An example with count for a numerical index and for_each for a key-based index.

count: resource "aws_instance" "web" { aws_instance.web[3]
for_each:

resource "aws_instance" "web" {
  # ...
  for_each = {
    "terraform": "value1",
    "resource":  "value2",
    "indexing":  "value3",
    "example":   "value4",

} }

aws_instance.web["example"]

# ...

count = 4 }

8f Use HCL and Terraform functions to write configuration

The Terraform language includes a number of built-in functions that you can call from within expressions to transform and combine values. The general syntax for function calls is a function name followed by comma-separated arguments in parentheses:

max(5, 12, 9)

8g Describe built-in dependency management (order of execution based)

there are two types of dependency: implicit and explicit Implicit
This type is implied by the configuration itself. For example:

resource "aws_instance" "example_a" {
  ami           = data.aws_ami.amazon_linux.id
  instance_type = "t2.micro"

}

resource "aws_instance" "example_b" {
  ami           = data.aws_ami.amazon_linux.id
  instance_type = "t2.micro"

}

resource "aws_eip" "ip" {
  vpc      = true
  instance = aws_instance.example_a.id

}

In the above example the aws_eip.ip resource depends on aws_instance.example_a for it’s instance attribute. Whereas aws_instance.example_b can be created at any time aws_eip.ip must wait until aws_instance.example_b is created. The dependency is implicit in the configuration.

Explicit

Sometimes depencies between resources are not directly visible to terraform. For example: suppose theres is an application running on an EC2 instance that expects to use a specific storage bucket. In that case you can use the depends_on attribute inside the EC2 instance to make the dependency explicit. See below:

resource "aws_s3_bucket" "example" { }
resource "aws_instance" "example_c" {
  ami           = data.aws_ami.amazon_linux.id
  instance_type = "t2.micro"
  depends_on = [aws_s3_bucket.example]
}
module "example_sqs_queue" {
  source  = "terraform-aws-modules/sqs/aws"
  version = "3.3.0"
  depends_on = [aws_s3_bucket.example, aws_instance.example_c]
}

9 Understand Terraform Cloud capabilities

9a Explain how Terraform Cloud helps to manage infrastructure

Terraform Cloud gives you access to version control, shared variables, runnning Terraform in a stable remote environment, and securely storing remote state

Terraform Cloud manages infrastructure collections with workspaces instead of directories. A workspace contains everything Terraform needs to manage a given collection of infrastructure, and separate workspaces function like completely separate working directories.

You can break megalithic configurations down into smaller configurations, assign them to workspaces, and grant permission / make updates to smaller sections of your infrastructure.

9b Describe how Terraform Cloud enables collaboration and governance

Terraform cloud includes easy access to shared state and secret data, access controls for approving changes to infrastructure, a private registry for sharing Terraform modules, detailed policy controls for governing the contents of Terraform configurations

Terraform cloud tracks changes in state overtime.

Terraform Cloud can perform automatic health assessments in a workspace to assess whether its real infras- tructure matches the requirements defined in its Terraform configuration

You can use modules from the public registry or from the private registry for modules you have created and public modules you recommend for use in your organization.

Private providers and private modules are hosted on an organization’s private registry and are only available to members of that organization

The organization can grant workspace permissions to teams that allow its members to start Terraform runs, create workspace variables, read and write state, etc.

You can use Sentinal code (another Hashicorp product) to set up policies for your cloud workspaces. This could be something like requiring a version or enforcing a rule on a resource attribute eg all ec2 instances must be in us-east.