DEV Community

JL
JL

Posted on • Edited on

Terraform Notes

Install the binary on Windows and set Path
https://developer.hashicorp.com/terraform/downloads

Find the example codes for provider (e.g. AWS)
https://registry.terraform.io/providers/hashicorp/aws/latest/docs

Steps:

init
will parse the script and download the necessary plugins
When Terraform initializes a new Terraform directory, it creates a lock file named .terraform.lock.hcl and the _.terraform _directory.

plan
Will have a preview of what resources to be created (by comparing configuartion and local state file)

apply
makes the changes defined by your Terraform configuration to create, update, or destroy resources.

Example codes:
first_ec2.tf

provider "aws" {
  region     = "us-west-2"
  access_key = "PUT-YOUR-ACCESS-KEY-HERE"
  secret_key = "PUT-YOUR-SECRET-KEY-HERE"
}

resource "aws_instance" "myec2" {
   ami = "ami-082b5a644766e0e6f"
   instance_type = "t2.micro"
}
Enter fullscreen mode Exit fullscreen mode

Commands:

terraform init
terraform plan
terraform apply
Enter fullscreen mode Exit fullscreen mode

Quick steps for configuring provider AWS for Terraform

provider "aws" {
  region     = "us-west-2"
  access_key = "my-access-key"
  secret_key = "my-secret-key"
}
Enter fullscreen mode Exit fullscreen mode

https://docs.aws.amazon.com/powershell/latest/userguide/pstools-appendix-sign-up.html

============
BASIC

Variable
Allowing you to specify value other than hardcoded in the main.tf. E.g. terraform apply -var "instance_name=YetAnotherName"

 resource "aws_instance" "app_server" {
   ami           = "ami-08d70e59c07c61a3a"
   instance_type = "t2.micro"

   tags = {
-    Name = "ExampleAppServerInstance"
+    Name = var.instance_name
   }
 }
Enter fullscreen mode Exit fullscreen mode

https://developer.hashicorp.com/terraform/tutorials/aws-get-started/aws-variables

Outputs

output "instance_id" {
  description = "ID of the EC2 instance"
  value       = aws_instance.app_server.id
}

output "instance_public_ip" {
  description = "Public IP address of the EC2 instance"
  value       = aws_instance.app_server.public_ip
}
Enter fullscreen mode Exit fullscreen mode

Then you can see in the log after running apply

Changes to Outputs:
  + instance_id        = "i-0bf954919ed765de1"
  + instance_public_ip = "54.186.202.254"
Enter fullscreen mode Exit fullscreen mode

https://developer.hashicorp.com/terraform/tutorials/aws-get-started/aws-outputs


Version of project and Terraform
https://developer.hashicorp.com/terraform/tutorials/configuration-language/versions#inspect-the-terraform-state-file
The state file is generated/updated after running a plan/apply. It is a snapshot of the actual remote cloud resource.
"version": 4,
"serial": 8,
"terraform_version": "0.15.0",

Note: when you update your project's main.tf and execute terraform apply, the serial number will bump up by 1 or 2.

The terraform.tf file (similar to Package.json) contains the required version constrains of Terraform Core and plug-ins (=providers). There is no convention that terraform.tf must be the one. Sometimes you will see in the tutorial that versions.tf_ is used. So treat all *.tf as *.js in a node.js project.

This configuration sets required_version to ~> 0.12.29. The ~> symbol allows the patch version to be greater than 29 but requires the major and minor versions (0.12) to match the version that the configuration specifies.

Versions of providers in lock files

  • constraints
  • side effects of version upgrade

terraform.lock.hcl (HashiCorp configuration language) is a middle product of terraform.lock.hcl is a middle product of init. It is similar to Package.json.lock. It is similar to Package-lock.json in npm.

Terraform automatically creates or updates the dependency lock file each time you run the terraform init command.

Re-initialize your configuration with the -upgrade flag. This tells Terraform to download the new version of the provider, and update the version and signature in the lock file.

https://developer.hashicorp.com/terraform/tutorials/configuration-language/provider-versioning


Providers
https://registry.terraform.io/browse/providers


Define Infrastructure with Terraform Resources
Full run of a typical EC2 with security group + user data script to initiate a web application:
https://developer.hashicorp.com/terraform/tutorials/configuration-language/resource

2 key learnings from the above are:

  • Know how to use docs of the plugin

Image description

  • Output information of each 'apply'
output "domain-name" {
  value = aws_instance.web.public_dns
}

output "application-url" {
  value = "${aws_instance.web.public_dns}/index.php"
}

Enter fullscreen mode Exit fullscreen mode

The previous 2 tutorials and this following one are basically talking about the same:
https://developer.hashicorp.com/terraform/tutorials/cli/init

The above one is more exploring the process of how Terraform gets the providers and modules, into local file system.

The main.tf contains reference to 1 local module and 1 remote module. Module is more like function for quick implementation to do some task, relying on other providers.

  • Providers, which are plugins for Terraform that extend it with support for interacting with various external systems.
  • Modules, which allow splitting out groups of Terraform configuration constructs (written in the Terraform language) into reusable abstractions.
module "nginx-pet" {
  source = "./nginx"

  container_name = "hello-${random_pet.dog.id}"
  nginx_port = 8001
}

module "hello" {
  source  = "joatmon08/hello/random"
  version = "3.0.1"

  hello = random_pet.dog.id

    secret_key = "secret"
}
Enter fullscreen mode Exit fullscreen mode

Terraform Cloud (doing execution and storing states in remote)
After registration, use token (if you configure remote in main.tf you will be asked to use login with token) and creating an organisation:

All the apply will be executed remotely (as github workflow)

Image description
https://developer.hashicorp.com/terraform/tutorials/aws-get-started/aws-remote

Import
Command import is to syncrhonise from an existing infrastructure into terraform state. The aim is to bring that infrastructure under terraform's control.
https://developer.hashicorp.com/terraform/tutorials/state/state-import

Note: The tutorial uses a hardcoded host value docker.sock in docker provider.
The solution is here: you will need to find out what a working docker.sock is in your own machine, and replace the docker.sock in docker provider.


Commands for state management

terraform.tfstate contains the resource state of last sync. If you run terraform plan, Terraform will calculate the difference of configuration and state, to figure out what to apply.

The relationship of 3 entities is:
Configuration (via apply)-> Physical resource (after apply or via refresh)-> Local state
(tutorial: https://developer.hashicorp.com/terraform/tutorials/state/state-cli)

replace
Terraform usually only updates your infrastructure if it does not match your configuration. You can use the -replace flag for terraform plan and terraform apply operations to safely recreate resources in your environment even if you have not edited the configuration, which can be useful in cases of system malfunction.

terraform plan -replace="aws_instance.example"

show
Get a human-friendly output of the resources contained in your state.

terraform show

state list
Run terraform state list to get the list of resource names and local identifiers in your state file. This command is useful for more complex configurations where you need to find a specific resource without parsing state with terraform show.

 terraform state list
data.aws_ami.ubuntu
aws_instance.example
aws_security_group.sg_8080
Enter fullscreen mode Exit fullscreen mode

state mv
The terraform state mv command moves resources from one state file to another. You can also rename resources with mv. Moving resources is useful when you want to combine modules or resources from other states, but do not want to destroy and recreate the infrastructure.

Note: The move command will update the resource in state, but not in your configuration file. You have to add the corresponding resource in the configuration as well, sperately.

terraform state mv -state-out=../terraform.tfstate aws_instance.example_new aws_instance.example_new

state rm
The terraform state rm subcommand removes specific resources from your state file. This does not remove the resource from your configuration or destroy the infrastructure itself.

terraform state rm aws_security_group.sg_8080

terraform refresh
Updates the state file when physical resources change outside of the Terraform workflow.
The -refresh-only flag was introduced in Terraform 0.15.4, and is preferred over the _terraform refres_h subcommand.
In previous versions of Terraform, the only way to refresh your state file was by using the terraform refresh subcommand. However, this was less safe than the -refresh-only plan and apply mode since it would automatically overwrite your state file without giving you the option to review the modifications first. In this case, that would mean automatically dropping all of your resources from your state file.

Use AWS cli to modify the physcial infrastructure outside terraform flow. E.g. delete an EC2 instance
aws ec2 terminate-instances --instance-ids $(terraform output -raw instance_id)

And then use terraform refresh to sync from physcial infrastructure into local state file

And then use terraform plan to check the difference between configuration and the state

terraform destroy

Note:
The unsync situation is defined as Resource Drift. The same command set (refresh and import) are covered in this tutorial:
https://developer.hashicorp.com/terraform/tutorials/state/resource-drift


Trouble-shoot (logging, support ticket)

To switch on logging:
https://www.phillipsj.net/posts/how-to-configure-logging-for-terraform/

PowerShell

> $env:TF_LOG="TRACE"
> $env:TF_LOG_PATH="terraform.txt"
Enter fullscreen mode Exit fullscreen mode

Bash

$ export TF_LOG="TRACE"
$ export TF_LOG_PATH="terraform.txt"
Enter fullscreen mode Exit fullscreen mode

Tutorial: https://developer.hashicorp.com/terraform/tutorials/configuration-language/troubleshooting-workflow#enable-terraform-logging


Modules

Tutorial:
https://developer.hashicorp.com/terraform/tutorials/modules/module-create
This tutorial contains a consuming project and a local module within it. The module encapsulates the actions to create a website hosting-pueposed s3 bucket which allows public access. The project uses the module by passing some inputs such as bucket name as the website name.

In summary, for a module:

  • Create scaffolder contains tf files and LICENSE + README.md
  • Define proper input vars and outputs
  • A consumer terraform to use the module to do stuff

The commands used in this tutorial:

terraform get
It is a light command to install modules.
In this case, Terraform will install the required 2 modules from registry, and install 1 local module by directly referring to its folder within your project.

Downloading registry.terraform.io/terraform-aws-modules/ec2-instance/aws 4.3.0 for ec2_instances...
- ec2_instances in .terraform\modules\ec2_instances
Downloading registry.terraform.io/terraform-aws-modules/vpc/aws 3.18.1 for vpc...
- vpc in .terraform\modules\vpc
- website_s3_bucket in modules\aws-s3-static-website-bucket
Enter fullscreen mode Exit fullscreen mode

terraform output
The terraform output command is used to extract the value of an output variable from the state file.
https://developer.hashicorp.com/terraform/cli/commands/output
In this case we run terraform output to get the generated bucket website domain name so the developer knows easily what url to test with . E.g. https://.s3-us-west-2.amazonaws.com/index.html

Variables and Outputs
Tutorial
https://developer.hashicorp.com/terraform/tutorials/modules/module-use
The principal is to:

(Inputs)
Define variables in a standalone file variables.tf. E.g.

variable "vpc_name" {
  description = "Name of VPC"
  type        = string
  default     = "example-vpc"
}
Enter fullscreen mode Exit fullscreen mode

Refer to the variables and assign them to the inputs of modules within main.tf:

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  ...
  name = var.vpc_name
Enter fullscreen mode Exit fullscreen mode

(Outputs)
Refer to the outputs of modules as outputs within main.tf:

output "ec2_instance_public_ips" {
  description = "Public IP addresses of EC2 instances"
  value       = module.ec2_instances[*].public_ip
}
Enter fullscreen mode Exit fullscreen mode

Collection Operation

Tutorial:
https://developer.hashicorp.com/terraform/tutorials/configuration-language/troubleshooting-workflow#correct-a-for_each-error

In this case, the configuration defines a map collection of aws_instance:

resource "aws_instance" "web_app" {
   for_each               = local.security_groups
   ami                    = data.aws_ami.ubuntu.id
   instance_type          = "t2.micro"
   vpc_security_group_ids = [each.value]
   tags = {
    Name = "${var.name}-learn-${each.key}"
   }
 }
}
Enter fullscreen mode Exit fullscreen mode

And the outputs refer to the collection of aws_instance

 output "instance_id" {
   description = "ID of the EC2 instance"
    value       = [for instance in aws_instance.web_app: instance.id]
 }

 output "instance_public_ip" {
   description = "Public IP address of the EC2 instance"
    value       = [for instance in aws_instance.web_app: instance.public_ip]
 }

output "instance_name" {
   description = "Tags of the EC2 instance"
   value        = [for instance in aws_instance.web_app: instance.tags.Name]
}
Enter fullscreen mode Exit fullscreen mode

The outputs print:

Outputs:

instance_id = [
  "i-0a679d5acef2b8e01",
  "i-02bd8b86bfa93ae96",
]
instance_name = [
  "terraform-learn-sg_8080",
  "terraform-learn-sg_ping",
]
instance_public_ip = [
  "13.58.116.31",
  "3.131.82.166",
]
Enter fullscreen mode Exit fullscreen mode

Top comments (0)