I have created these notes as part of my preparation for Hashicorp Terraform Certified Associate Exam (003). These notes can be used to prepare for the certification exam or as a refresher for your terraform knowledge. Feel free to submit a PR if you have any suggestions.
Table of Contents:
- Important Resources
- Infrastructure As Code(IaC)
- Core Terraform Workflow
- Initialise the working directory
- Validate
- TERRAFORM PLAN
- TERRAFORM APPLY
- TERRAFORM DESTROY
- Resource Addressing in Terraform
- Terraform Providers
- Terraform STATE
- Provider and Resource Blocks
- Variables
- Provisioners
- Terraform State Command
- Meta-Arguments
- Local and Remote Storage
- Migrating to Terraform Cloud or Terraform Enterprise
- Embedded Policy As Code: Sentinel
- Hashicorp Vault
- Terraform Cloud
- Important Miscellaneous Points
- FreeCodeCamp (Course By Andrew Brown) - FREE
- Kodekloud - PAID
- A Cloud Guru - PAID
- HashiCorp Certified: Terraform Associate Practice Exam by Bryan Krausen - BEST & PAID
- ExamPro - 1 FREE Exam
- Pearson Terraform Associate Exam Cram - Needs Oreilly Subscription, Can be attempted with free trial (no credit card).
- Infrastructure is described using a high-level configuration syntax.
- This allows a blueprint of your datacenter to be versioned and treated as you would any other code. Additionally, infrastructure can be shared and re-used.
- Infrastructure as Code almost always uses parallelism to deploy resources faster.
- And depending on the solution being used, it doesn't always have access to the latest features and services available on cloud platforms or other solutions.
WRITE -> PLAN -> APPLY
terraform init
- Downloads the ancilliary components required for your code to work. Like Providers, modules etc.
- Sets up back-end for storing Terraform state file.
terraform validate
The terraform validate command validates the configuration files in a directory, referring only to the configuration and not accessing any remote services such as remote state, provider APIs, etc.
INFO:
terraform plan
command runs the validation implicitly.
terraform plan -out test
Allows users to review the action plan before executing anything. It performs the API calls with the specified provider backend but does not create/modify anything.
Also authenticates with the platform.
terraform apply test
- Deploy the instructions and statements in the code.
- Creates the state file
terraform.tfstate
.
INFO: When you apply a particular plan generated using
-out
flag, terraform won't ask you for approval.
terraform destroy
/ terraform apply -destroy
Destroys the provisioned resourced gracefully.
provider, resource, data, module
Refer to Resource Addressing for more details.
- Terraform relies on plugins called providers to interact with cloud providers, SaaS providers, and other APIs.
- Providers are distributed separately from Terraform itself, and each provider has its own release cadence and version numbers.
- The Terraform Registry is the main directory of publicly available Terraform providers, and hosts providers for most major infrastructure platforms.
- Best practice is to specify the version of provider so that tf does not download the latest one when initialising.
- State helps in RESOURCE TRACKING!
- Stored into flat files as JSON data.
- The default name of state file is
terraform.tfstate
. - The default backend of terraform when run locally is local, named as
terraform.tfstate
.
provider "aws" {
region = "us-east-1"
}
resource "aws_instance" "my_instance" {
ami = ""
instance_type = "t2.micro"
}
TF fetches data from an already existing resource
data "aws_instance" "vm" {
instance_id = "i-ddddddddddddddd"
}
The difference between data and resource block is that data block fetches the information from a preexisting resource and will not create one.
Resource Address: data.aws_instance.vm
output "instance_ip" {
description = "Instance IP"
value = resource.aws_instance.my_instance.private_ip
}
- If the variable stores sensitive value, use
sensitive = true
inside the variable block. - Add validation to the variable using
validation
argument.
List Type Variable
variable "availability_zone_names" {
type = list(string)
default = ["us-west-1a", "us-east-1a"]
}
Highest Precedence is given to the values from the ENV vars & then .tfvars file while setting values of variables in terraform.
1 is the lowest precedence and 4 is the highest precedence.
Precedence | Option |
---|---|
1 | Environment Variables |
2 | terraform.tfvars file |
3 | *.auto.tfvars (alphabetical order) |
4 | -var or -var-file (CLI flags) |
Does specified action during creation/destruction of the resource. It will run commands that you’ve specified.
- Each individual resource can have its own provisioner defining the connection method(ssh or WinRM) and the actions/commands or scripts to execute.
- Types: Creation Time and Destroy Time provisioners.
- Use provisioners ONLY if necessary.
- Terraform cannot keep track of provisioners modifications in the state file. If TF tried to do so, it would interrupt the declarative model of TF.
- If the command within the provisioner returns non zero code, it is considered as fail and underlying resource is tainted. i.e marks the resource to be provision again on next run.
resource "null_resource" "dummy" {
// Creation Time Provisioner
provisioner "local-exec" {
command = "echo '0' > status.txt"
}
// Destroy Time Provisioner
provisioner "local-exec" {
when = destroy
command = "echo '1' > status.txt"
}
}
To reference the resource name within the same resource block, use self
to avoid cyclical dependencies. Which might refer to the resource which is not yet created.
resource "aws_instance" "analytics-app" {
ami = ami-sdfgasdfasdf
instance_type = t2.micro
subnet_id = aws_subnet.analytics-subnet.id
associate_public_ip_address = true
key_name = aws_key_pair.master-key.key_name
vpc_security_group_ids = [........]
provisioner "local-exec" {
command = "aws ec2 wait instance-status-ok --region us-east-1 --instance-ids ${self.id}"
}
}
On unsuccessful execution of:
- Creation Time Provisioner:
- Resource gets tainted and TF attempts to re-provision it in next run.
- Deletion Time Provisioner:
- Apply errors and resource is attempted to be destroyed in next run.
-
state file is the mapping between real world resources and terraform configuration.
-
resource dependency metadata is also tracked using the state file. So that terraform know to deploy subnet before creating an ec2 instance.
-
helps in caching the resource attributes to minimize the API calling.
-
TF refreshes the state file prior to any modification operation.
-
Command use for manipulate and read tf state file.
-
Scenarios:
- Advance state management.
- Manually remove resources from tf state file so that it’s not managed by terraform.
- list the tracked resources.
Common Commands:
Command | Use |
---|---|
terraform state list | list the resources tracked by tf state file. |
terraform state rm | delete a resource from terraform state file. |
terraform state show | show details of a resource tracked in state file. |
Special arguments(or configuration options) that are used with resource blocks and modules to control their behaviour and influence the infrastructure provisioning process.
List of meta arguments:
-
depends_on
: -
count
: -
for_each
: -
provider
: -
lifecycle
: Some details of the lifecycle of resource behaviour can be controlled using lifecycle block within the resource block.resource "docker_container" "my_container" { ... lifecycle { create_before_destroy = true } } // create_before_destroy changes the default behaviour of terraform to destroy the resource first // and create it if resource modification is not possible using API. With this argument set to true, // terraform will create another resource first and then destroy this resource.
- By default TF stores the state file locally.
- State locking is enabled by default on local system.
- Remote storage is available by few providers like s3, GC Storage etc.
- Storage like S3 etc also has remote version locking mechanism which prevents parallel writes to the state file.
- For remote backend, we need to add
backend "" {}
block insideterraform {}
block.
terraform {
required_providers {
docker = {
source = "kreuzwerker/docker"
}
}
required_version = ">= 0.13"
backend "s3" {
profile = "demo"
region = "us-east-1"
key = "terraform.tfstate"
bucket = "<AWS-S3-BUCKET-NAME-GOES-HERE>"
}
}
This requires migrating the Terraform state files for those resources to one or more Terraform Cloud workspaces. You can perform this migration with either the Terraform CLI or the Terraform Cloud API.
Refer to Migration docs for more details.
It is a policy as code framework which enables the CIS like rule enforcement like “not allowing access to port 22 in aws instances”, “ensuring every ec2 instance has tag associated with it” etc.
- Secrets management software
- Dynamically provision credentials and rotates them for accessing services like AWS instead of using permanent credentials like aws cli access key.
- Encrypts sensitive data in transit and at REST and provides fine grained ACLs.
Terraform Cloud is an application that helps teams use Terraform together.
- Manages Terraform runs in a consistent and reliable environment.
- 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, and more.
- Remote Terraform Execution.
- Workspace based model enabling teams to have their own workspaces.
- Integration with VCS like Github and Gitlab.
- Remote state management and CLI execution.
- Private tf registry.
- Cost estimation and sentinel integrations.
Refer to Terraform Cloud Docs for more info.
- Before a
terraform validate
can be run, the directory must be initialized usingterraform init
. - You have a Terraform configuration file with no defined resources. However, there is a related state file for resources that were created on AWS. What happens when you run a
terraform apply
?- Terraform will DESTROY all the resources to match the current state of the configuration.
- When working with outputs, you need to determine where the value will be coming from and work your way backward from there. For example, if the resource was created inside of a module, then the module will require an output block to export that value. That said, output blocks that are created in a module aren't displayed on the Terraform CLI. Therefore, you need to create an output block in the parent/calling module to output the value while referencing the output in the module. Because of this, the correct answer requires you to create an output in the parent module and reference the output value from the module.