Terraform — Resources

Terraform — Resources

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 supports create_before_destroy, prevent_destroy, ignore_changes and replace_triggered_by lifecycles.
resource "azurerm_resource_group" "example" {
  # ...

  lifecycle {
    create_before_destroy = true
  }
}
  • provisioner and connection: 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 and Postcondition : Declaring in lifecycle blocks, precondition and postcondition blocks can create custom validation rules for resources, data sources, and output values. Terraform checks an object’s associations before evaluating it precondition, and performs the postcondition 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 destroy

  • fail: report an error and stop executing the change (this is the default behavior). If this is a create-time presetter, 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

sunil kumar

Shubham Londhe

Connect with me over linkedin : linkedin.com/in/sunilkumar2807