In Terraform, resources are the most important part of Terraform. Resources are defined by resource
blocks. A resource can define one or more infrastructure resource objects, such as VPCs, virtual machines, or DNS records, Consul key-value pair data, etc.
Resource Syntax
Resources are defined through resource blocks, and I will first explain the scenario of defining a single resource object through resource blocks.
resource "aws_instance" "web_server" {
ami = "ami-a1b2c3d4"
instance_type = "t2.micro"
}
In the above example, immediately following the resource keyword is the resource type, which in the above example is aws_instance
. The following is the Local Name of the resource, which is in the example web_server
.
The Local Name can be used to reference the resource in the code in the same module, but the combination of type and Local Name must be unique in the current module, and the Local Name of two resources of different types can be the same.
The content in the curly braces is the block, and the values of the various parameters used to create the resource are defined in the block. In the example we define the image id used by the EC2 server and its size.
Resource Behavior
A resource block declares an exact infrastructure object that the author wants to create, and sets the values of various properties. If we are writing a new Terraform code file, the resources defined by the code only exist in the code, and no corresponding actual infrastructure resources exist.
Performing terraform apply
on a set of Terraform code creates, updates, or destroys actual infrastructure objects, and Terraform develops and executes a change plan so that the actual infrastructure conforms to the code’s definition.
Whenever Terraform creates a new infrastructure object according to a resource block, the id of the actual object is stored in Terraform state so that Terraform can update or destroy it in the future according to the change plan.
If the resource described by a resource block is already recorded in the state file, then Terraform will compare the state of the record with the state described by the code, and if necessary, Terraform will make a change plan to make the state of the resource match the description of the code.
This behavior applies to all resources regardless of their type. The details of creating, updating, and destroying a resource vary by resource type, but the rules of behavior are general.
Meta parameters
The resource block supports several kinds of meta parameter declarations. These meta parameters can be declared in all types of resource blocks, and they will change the behavior of the resource:
depends_on
: Explicitly declare dependencies
resource "aws_instance" "example" {
ami = "ami-a1b2c3d4"
instance_type = "t2.micro"
# Terraform can infer from this that the instance profile must
# be created before the EC2 instance.
iam_instance_profile = aws_iam_instance_profile.example
# However, if software running in this EC2 instance needs access
# to the S3 API in order to boot properly, there is also a "hidden"
# dependency on the aws_iam_role_policy that Terraform cannot
# automatically infer, so it must be declared explicitly:
depends_on = [
aws_iam_role_policy.example,
]
}
count
: Create multiple resource instances
resource "aws_instance" "server" {
count = 4 # create four similar EC2 instances
ami = "ami-a1b2c3d4"
instance_type = "t2.micro"
tags = {
Name = "Server ${count.index}"
}
}
for_each
: Iterates over the collection and creates a corresponding resource instance for each element in the collection
# Use set
resource "aws_iam_user" "the-accounts" {
for_each = toset( ["Todd", "James", "Alice", "Dottie"] )
name = each.key
}# Use map
resource "azurerm_resource_group" "rg" {
for_each = {
a_group = "eastus"
another_group = "westus2"
}
name = each.key # the key of the map/set
location = each.value # The value of the map/set
}
provider
: Specify a non-default Provider instance
# default configuration
provider "google" {
region = "us-central1"
}
# alternate configuration, whose alias is "europe"
provider "google" {
alias = "europe"
region = "europe-west1"
}
resource "google_compute_instance" "example" {
# This "provider" meta-argument selects the google provider
# configuration whose alias is "europe", rather than the
# default configuration.
provider = google.europe
# ...
}
lifecycle
: The lifecycle behavior of custom resources. Terraform supportscreate_before_destroy
,prevent_destroy
,ignore_changes
andreplace_triggered_by
lifecycles.
resource "azurerm_resource_group" "example" {
# ...
lifecycle {
create_before_destroy = true
}
}
provisioner
andconnection
: Perform some additional actions after resource creation. Some infrastructure objects require specific actions after they are created in order to work properly. For example, a host instance must have a configuration uploaded or initialized by a configuration management tool in order to function properly.
resource "aws_instance" "web" {
# ...
provisioner "file" {
source = "conf/myapp.conf"
destination = "/etc/myapp.conf"
connection {
type = "ssh"
user = "root"
password = var.root_password
host = self.public_ip
}
}
}
Precondition
andPostcondition
: Declaring inlifecycle
blocks,precondition
andpostcondition
blocks can create custom validation rules for resources, data sources, and output values. Terraform checks an object’s associations before evaluating itprecondition
, and performs thepostcondition
check .
data "aws_ami" "example" {
id = var.aws_ami_id
lifecycle {
# The AMI ID must refer to an existing AMI that has the tag "nomad-server".
postcondition {
condition = self.tags["Component"] == "nomad-server"
error_message = "tags[\"Component\"] must be \"nomad-server\"."
}
}
}
Presets
By default, the presetter
is run when the resource object is created, but not when the object is updated or destroyed. The default behavior of the presetter
is to boot a system.
If the prefab fails on creation, the resource object will be tainted (I will go into more detail when we introduce the command terraform taint
). A tainted resource is destroyed and rebuilt the next time the command terraform apply
is executed.
Terrform is designed this way because when the preconfigurer
fails, it signals that the resource is in a semi-ready state. Since Terraform cannot measure the behavior of the presetter
, the only way to be completely sure that the resource is properly initialized is to delete and rebuild.
We can change this behavior by setting parameters on_failure
.
Presets on destruction
If we set the when
parameter of the presetter
, then the presetter
will be executed when the resource is destroyed:
resource "aws_instance" "web" {
# ...
provisioner "local-exec" {
when = destroy
command = "echo 'Destroy-time provisioner'"
}
}
The presetter
runs before the resource is actually destroyed. If the run fails, Terraform will report an error and terraform apply
re-execute the presetter
the next time the operation is run. In this case, careful attention needs to be paid to the on-destroy presetter
so that it can be safely executed repeatedly.
Preset failure behavior
By default, failure to run the presetter
will cause terraform apply
execution to fail. This behavior can be changed by setting on_failure
parameters. The values that can be set are:
continue
: ignore the error and continue to create or destroyfail
: report an error and stop executing the change (this is the default behavior). If this is a create-timepresetter
, mark the taint on the corresponding resource object
resource "aws_instance" "web" {
# ...
provisioner "local-exec" {
command = "echo The server's IP address is ${self.private_ip}"
on_failure = continue
}
}
Local Resources
While most resource types correspond to a resource object controlled through the remote infrastructure API, there are also some resource objects that exist only within the Terraform process itself and are used to compute and generate certain results and store these results in state for future use.
For example, we can use tls_private_key
to generate public and private keys, use tls_self_signed_cert
to generate self-signed certificates, or use random_id
to generate random ids. While not as important as other "real" infrastructure objects, these local resources can also be useful glues for connecting other resources.
Local resources behave the same as other types of resources, but their result data exists only in the Terraform state file. “Destroying” such a resource simply removes the resulting data from the state.
Operation Timeout
Some resource types provide a special timeouts inline block parameter that allows us to configure how long we allow the operation to last before the timeout will be considered a failure. For example, the aws_db_instance
resource allows us to set timeouts for create
, update
, and delete
operations respectively.
The timeout is completely handled by the Provider corresponding to the resource, but the Providers that support the timeout setting generally follow the same tradition, that is, the timeout setting is defined by a parameter called an embedded timeouts
block, and the timeout period of different operations can be set separately in the timeouts
block. The timeout is described by a string, such as "60m"
60 minutes, "10s"
10 seconds, or "2h"
2 hours.
resource "aws_db_instance" "example" {
# ...
timeouts {
create = "60m"
delete = "2h"
}
}
The class of operations with configurable timeouts is determined by each resource type that supports timeouts.
I am Sunil kumar, Please do follow me here and support #devOps #trainwithshubham #github #devopscommunity #devops #cloud #devoparticles #trainwithshubham
Connect with me over linkedin : linkedin.com/in/sunilkumar2807