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"
}
Commands:
terraform init
terraform plan
terraform apply
Quick steps for configuring provider AWS for Terraform
- Create a free-tier account in AWS
Log in using root user
Go to IAM > create a user > attach policy as Administrator
Use following instructions to create access key and secert pair
https://docs.aws.amazon.com/powershell/latest/userguide/pstools-appendix-sign-up.htmlThen add this element in your main.tf
provider "aws" {
region = "us-west-2"
access_key = "my-access-key"
secret_key = "my-secret-key"
}
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
}
}
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
}
Then you can see in the log after running apply
Changes to Outputs:
+ instance_id = "i-0bf954919ed765de1"
+ instance_public_ip = "54.186.202.254"
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
- 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"
}
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"
}
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)
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
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"
Bash
$ export TF_LOG="TRACE"
$ export TF_LOG_PATH="terraform.txt"
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
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"
}
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
(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
}
Collection Operation
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}"
}
}
}
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]
}
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",
]
Top comments (0)