6.2 Core Components

The following section will explain the core components and building blocks of Terraform. This will enable you to build your very first Terraform definition files.

6.2.1 Providers

Terraform relies on plugins called providers to interact with Cloud providers, SaaS providers, and other APIs. Each provider adds specific resource types and/or data sources that can be managed by Terraform. For example, the aws provider shown below allows to specify resources related to the AWS Cloud such as S3 Buckets or EC3 Instances.

Depending on the provider it is necessary to supply it with specific parameters. The aws provier for example needs the region as well as username and password. If nothing is specified it will automatically pull these information from the AWS CLI and the credentials specified under the directory .aws/config. It is also a best practice to specify the version of the provider, as the providers are usually maintained and updated on a regular basis.

provider "aws" {
  region = "us-east-1"
}

6.2.2 Resources

A resource is the core building block when working with Terraform. It can be a "local_file" such as shown in the example above, or a cloud resource such as an "aws_instance" on aws. The resource type is followed by the custom name of the resource in Terraform. Resource definitions are usually specified in the main.tffile. Each customization and setting to a ressource is done within its resource specification. The style convention when writing Terraform code states that the resource name is named in lowercase as well as it should not repeat the resource type. An example can be seen below

# Ressource type: aws_instance
# Ressource name: my-instance
resource "aws_instance" "my-instance" {
  # resource specification
  ami           = "ami-0ddbdea833a8d2f0d"
  instance_type = "t2.micro"
  
  tags = {
    Name = "my-instance"
    ManagedBy = "Terraform"
  }
}

6.2.3 Data Sources

Data sources in Terraform are “read-only” resources, meaning that it is possible to get information about existing data sources but not to create or change them. They are usually used to fetch parameters needed to create resources or generally for using parameters elsewhere in Terraform configuration.

A typical example is shown below as the "aws_ami" data source available in the AWS provider. This data source is used to recover attributes from an existing AMI (Amazon Machine Image). The example creates a data source called "ubuntu” that queries the AMI registry and returns several attributes related to the located image.

data "aws_ami" "ubuntu" {
  most_recent = true
  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server-*"]
  }
  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }
  owners = ["099720109477"] # Canonical
}

Data sources and their attributes can be used in resource definitions by prepending the data prefix to the attribute name. The following example used the "aws_ami" data source within an "aws_instace" resource.

resource "aws_instance" "web" {
  ami = data.aws_ami.ubuntu.id 
  instance_type = "t2.micro"
}

6.2.4 State

A Terraform state stores all details about the resources and data created within a given context. Whenever a resource is create terrafrom stores its identifier in the statefile terraform.tfstate. Providing information about already existing resources is the primary purpose of the statefile. Whenever a Terraform script is applied or whenever the resource definitions are modified, Terraform knows what to create, change, or delete based on the existing entries within the statefile. Everything specified and provisioned within Terraform will be stored in the statefile. This should be kept in mind and detain to store sensitive information such as initial passwords.

Terraform uses the concept of a backend to store and retrieve its statefile. The default backend is the local backend which means to store the statefile in the project’s root folder. However, we can also configure an alternative (remote) backend to store it elsewhere. The backend can be declared within a terraform block in the project files. The given example stores the statefile in an AWS S3 Bucket callen some-bucket. Keep in mind this needs access to an AWS account and also needs the AWS provider of terraform.

terraform {
  backend "s3" {
    bucket = "some-bucket"
    key = "some-storage-key"
    region = "us-east-1"
  }
}