Terraform Concepts
Terraform Commands
Comment
Terraform State
Dependancy Lock file
Create AWS Instance
Create AWS RDS Instance
Create Security Group and Elastic IP
Attributes, Cross Resource Attribute References & String Interpolation
output-values
Output
Variables
Variable Definitions File (TFVARS)
Variable Definition Precedence
Data Types
Data Types List & Number Example
Data Types Map
Data Types Set
Count & Count Index Parameter
Conditional Expression
Local Values
Function
Zipmap Function
toset Function
Data Sources
fetch-ami-data-source
Debugging Terraform
Dynamic Block
Tainting / Replace
Splat Expression "*"
Graph
plan-file
terraform-Setting
large-infra
AWS Hardening with Terraform Code
Lifecycle-meta-argument-ignore_changes
Lifecycle-meta-argument-create-before-destroy
Lifecycle-meta-argument-prevent_destroy
for_each
Provisioners-local-exec
Provisioners-remote-exec
Provisioners-failure-behaviour
Provisioners-create-destroy-time-provisioner
Modules
Modules-local-Output
Modules-local-Output->ConsoleLog
Workspace
Workspace-Output
S3 Backend
State File Locking
StateFileLockDynamo
State Management
Remote State
Import
Multiple-Providers
Sensitive Parameter
Vault
Dependency-lock
Heredoc Syntax
Terraform-Older-Version
Example-1
Terraform Commands
terraform init terraform init -migrate-state terraform init -reconfigure terraform plan terraform plan -destroy terraform plan -out=demopath terraform apply demopath terraform fmt terraform apply -refresh-only -auto-approve -> Refresh fetch real remote objects & only modify state file terraform apply -> Terraform will match current state to desired state terraform apply -auto-approve terraform destroy -> Delete All Resources in One Folder terraform destroy -target aws_instance.AmazonAMI -> resource type.local resource name terraform apply -var-file="custom.tfvars" -> Explicitly Run Custom tfvars terraform validate -> Validating syntax error in terrafom configuration files terraform apply -replace="aws_instance.myec2 -> Recreating the resource export TF_LOG_PATH=/tmp/crash.log export TF_LOG=TRACE terraform force-unlock [options] LOCK_ID terraform workspace show terraform workspace list terraform workspace new dev terraform workspace select dev terraform state list terraform state mv terraform state pull terraform state rm terraform state show terraform import aws_vpc.Terraform-VPC vpc-01b001b6d28e8fe52 terraform plan -generate-config-out=generated.tf terraform plan -generate-config-out=mysg.tf -chdir D:\AWS Triad & Terraform\import
Comment
# -> Single line comment // -> Alternate to # /* and */ -> Multi line comment
Terraform State
Terraform State - terraform.tfstate -> Terraform State File has all Rources Configurations Desired State - Resource Configurations in Main Terraform File Current State - Present Resource Configurations in AWSTerraform Desired state will replace AWS Current State AWS Console Manual Changes will be replced Condition Only Main Terraform File Resource configurations will be considered to change not Terraform State File Configurations(.tfstate)
Dependancy Lock file
.terraform.lock.hcl Dependancy Lock file: to lock in specific version terraform init -upgrade Extend lock version
Create AWS Instance
provider "aws" { region = "ap-south-1" access_key = "xxxxxxxxxxxxxxxxx" secret_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } resource "aws_instance" "AmazonAMI" { ami = "ami-0cc9838aa7ab1dce7" instance_type = "t3.micro" tags = { Name = "AmazonAMIInstance" } depends_on = [aws_eip.lb] }resource type aws_instance local resource name AmazonAMI https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance
Create AWS RDS Instance
provider "aws" { region = "ap-south-1" } resource "aws_db_instance" "default" { allocated_storage = 5 storage_type = "gp2" engine = "mysql" engine_version = "5.7" instance_class = "db.t2.micro" db_name = "mydb" username = "foo" password = "${file("../rds_pass.txt")}" parameter_group_name = "default.mysql5.7" skip_final_snapshot = "true" }
Create Security Group and Elastic IP
provider "aws" { region = "ap-south-1" } resource "aws_security_group" "allow_http" { name = "terraform-SG" description = "Managed from Terraform" } resource "aws_vpc_security_group_ingress_rule" "allow_tls_ipv4" { security_group_id = aws_security_group.allow_tls.id cidr_ipv4 = "0.0.0.0/0" from_port = 80 ip_protocol = "tcp" to_port = 80 } resource "aws_vpc_security_group_egress_rule" "allow_all_traffic_ipv4" { security_group_id = aws_security_group.allow_tls.id cidr_ipv4 = "0.0.0.0/0" ip_protocol = "-1" # semantically equivalent to all ports } resource "aws_eip" "lb" { domain = "vpc" }
Attributes, Cross Resource Attribute References & String Interpolation
provider "aws" { region = "ap-south-1" } resource "aws_eip" "lb" { domain = "vpc" } resource "aws_security_group" "example" { name = "attribute-sg" } resource "aws_vpc_security_group_ingress_rule" "example" { security_group_id = aws_security_group.example.id cidr_ipv4 = "${aws_eip.lb.public_ip}/32" from_port = 443 ip_protocol = "tcp" to_port = 443 }Cross Resource Attribute References resourceType.localResourceName.attribute aws_security_group.example.id String Interpolation "${aws_eip.lb.public_ip}/32"
output-values
provider "aws" { region = "us-east-1" } resource "aws_eip" "lb" { domain = "vpc" } output "public-ip" { value = aws_eip.lb.public_ip }
Output
resource "aws_iam_user" "lb" { name = "iamuser.${count.index}" count = 3 path = "/system/" } output "iam_names" { value = aws_iam_user.lb[*].name } output "iam_arn" { value = aws_iam_user.lb[*].arn }Console Command terraform output iam_names Console Command terraform output iam_arn
Variables
SecurityGroup.tf provider "aws" { region = "ap-south-1" } resource "aws_security_group" "allow_tls" { name = "terraform-firewall" description = "Managed from Terraform" } resource "aws_vpc_security_group_ingress_rule" "allow_ssh_ipv4" { security_group_id = aws_security_group.allow_tls.id cidr_ipv4 = var.vpc_ip from_port = var.ssh_port ip_protocol = "tcp" to_port = var.ssh_port } resource "aws_vpc_security_group_ingress_rule" "allow_http_ipv4" { security_group_id = aws_security_group.allow_tls.id cidr_ipv4 = var.open_ip from_port = var.http_port ip_protocol = "tcp" to_port = var.http_port } resource "aws_vpc_security_group_egress_rule" "allow_all_traffic_ipv4" { security_group_id = aws_security_group.allow_tls.id cidr_ipv4 = var.open_ip ip_protocol = "-1" # semantically equivalent to all ports }variables.tf variable "vpc_ip" { default = "158.34.56.203/32" description = "This is Open Private Public IP" } variable "open_ip" { default = "0.0.0.0/0" description = "This is Open Public IP" } variable "ssh_port" { default = "22" } variable "http_port" { default = "80" }
Variable Definitions File (TFVARS)
SecurityGroup.tf provider "aws" { region = "ap-south-1" } resource "aws_security_group" "allow_tls" { name = "terraform-firewall" description = "Managed from Terraform" } resource "aws_vpc_security_group_ingress_rule" "allow_ssh_ipv4" { security_group_id = aws_security_group.allow_tls.id cidr_ipv4 = var.vpc_ip from_port = var.ssh_port ip_protocol = "tcp" to_port = var.ssh_port } resource "aws_vpc_security_group_ingress_rule" "allow_http_ipv4" { security_group_id = aws_security_group.allow_tls.id cidr_ipv4 = var.open_ip from_port = var.http_port ip_protocol = "tcp" to_port = var.http_port }variables.tf variable "vpc_ip" { description = "Now vpc_ip taken from dev.tfvars" } variable "open_ip" { default = "1.1.1.1/1" description = "Even 1.1.1.1/1 is default for open_ip it will give priority to dev.tfvars only" } variable "ssh_port" {} variable "http_port" {}dev.tfvars vpc_ip = "158.34.56.203/32" open_ip = "0.0.0.0/0" ssh_port = "22" http_port = "80"terraform apply -auto-approve -var-file="dev.tfvars"
Variable Definition Precedence
variable.tf <- Priority 4 Environment varibles <- Priority 3 dev.tfvars <- Priority 2 CLI -var-file <- Priority 1
Data Types
string number bool list set map null
Data Types List & Number Example
variable "my-list" { type = list } variable "my-list" { type = list(number) } output "variable_value" { value = var.my-list }IAM Example variable "username" { type = number } resource "aws_iam_user" "user" { name = var.username }EC2 Example resource "aws_instance" "web" { ami = "ami-0c101f26f147fa7fd" instance_type = "t3.micro" vpc_security_group_ids = ["sg-06dc77ed59c310f03"] }vpc_security_group_ids Should be in list as per terraform registry
Data Types Map
variable "my-map" { type = map default = { Name = "Alice" Team = "Payments" } } output "variable_value" { value = var.my-map # value = var.my-map["Team"] }
Data Types Set
variable "my-set" { type = set default = {"Apple", "Banana", "Mango"} } output "variable_value" { value = var.my-set }
Count & Count Index Parameter
Example resource "aws_instance" "instance-1" { ami = "ami-082b5a644766e0e6f" instance_type = "t2.micro" count = 3 }Main Example variable "elb_names" { type = list default = ["dev-loadbalancer", "stage-loadbalanacer","prod-loadbalancer"] } resource "aws_iam_user" "lb" { name = var.elb_names[count.index] count = 3 path = "/system/" }
Conditional Expression
variable "istest" { istest = false } resource "aws_instance" "dev" { ami = "ami-082b5a644766e0e6f" instance_type = "t2.micro" count = var.istest == true ? 3 : 0 } resource "aws_instance" "prod" { ami = "ami-082b5a644766e0e6f" instance_type = "t2.large" count = var.istest == false ? 1 : 0 }If istest = true then 3 EC2 "t2.micro" instances will created If istest = false then 1 EC2 "t2.large" instance will created
Local Values
locals { common_tags = { Owner = "DevOps Team" service = "backend" } } resource "aws_instance" "app-dev" { ami = "ami-082b5a644766e0e6f" instance_type = "t2.micro" tags = local.common_tags } resource "aws_instance" "db-dev" { ami = "ami-082b5a644766e0e6f" instance_type = "t2.small" tags = local.common_tags } resource "aws_ebs_volume" "db_ebs" { availability_zone = "us-west-2a" size = 8 tags = local.common_tags }
Function
locals { time = formatdate("DD MMM YYYY hh:mm ZZZ", timestamp()) } variable "region" { default = "ap-south-1" } variable "tags" { type = list default = ["firstec2","secondec2"] } variable "ami" { type = map default = { "us-east-1" = "ami-0323c3dd2da7fb37d" "us-west-2" = "ami-0d6621c01e8c2de2c" "ap-south-1" = "ami-0470e33cd681b2476" } } resource "aws_key_pair" "loginkey" { key_name = "login-key" public_key = file("${path.module}/id_rsa.pub") } resource "aws_instance" "app-dev" { ami = lookup(var.ami,var.region) instance_type = "t2.micro" key_name = aws_key_pair.loginkey.key_name count = 2 tags = { Name = element(var.tags,count.index) } } output "timestamp" { value = local.time }
Zipmap Function
zipmap(["pineapple","oranges","strawberry"], ["yellow","orange","red"]) resource "aws_iam_user" "lb" { name = "demo-user.${count.index}" count = 3 path = "/system/" } output "arns" { value = aws_iam_user.lb[*].arn } output "zipmap" { value = zipmap(aws_iam_user.lb[*].name, aws_iam_user.lb[*].arn) }
toset Function
toset function will convert LIST of values to SET toset(["a","b","c","a"]) toset([ "a", "b", "c", ])
Data Sources
Intead Hardcoding Static value Dynamically call AWS Datasouce to fetch latest datadata-source-01.tf terraform { required_providers { digitalocean = { source = "digitalocean/digitalocean" } } } provider "digitalocean" { token = "your-token-here" } data "digitalocean_account" "example" {}data-source-02.tf data "local_file" "foo" { filename = "${path.module}/demo.txt" } output "data" { value = data.local_file.foo.content }data-source-03.tf provider "aws" { region = "us-east-1" } data "aws_instances" "example" {}data-source-format.tf provider "aws" { region = "us-east-1" } data "aws_instance" "example" { filter { name = "tag:Team" values = ["Production"] } }
fetch-ami-data-source
provider "aws" { region = "ap-south-1" } data "aws_ami" "myimage" { most_recent = true owners = ["amazon"] filter { name = "name" values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"] } } resource "aws_instance" "web" { ami = data.aws_ami.myimage.image_id instance_type = "t2.micro" }
Debugging Terraform
export TF_LOG_PATH=/tmp/crash.log export TF_LOG=TRACE
Dynamic Block
Example.tf resource "aws_security_group" "demo_sg" { name = "sample-sg" ingress { from_port = 8300 to_port = 8300 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } ingress { from_port = 9200 to_port = 9200 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } ingress { from_port = 9500 to_port = 9500 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } }dynamic-block.tf variable "sg_ports" { type = list(number) description = "list of ingress ports" default = [8200, 8201,8300, 9200, 9500] } resource "aws_security_group" "dynamicsg" { name = "dynamic-sg" description = "Ingress for Vault" dynamic "ingress" { for_each = var.sg_ports iterator = port content { from_port = port.value to_port = port.value protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } } dynamic "egress" { for_each = var.sg_ports content { from_port = egress.value to_port = egress.value protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } } }
Tainting / Replace
resource "aws_instance" "myec2" { ami = "ami-00c39f71452c08778" instance_type = "t2.micro" }Recreating the resource: terraform apply -replace="aws_instance.myec2" "terraform taint" command replaced by terraform apply -replace=""
Splat Expression "*"
resource "aws_iam_user" "lb" { name = "iamuser.${count.index}" count = 3 path = "/system/" } output "arns" { value = aws_iam_user.lb[*].arn }"*" is the splat expression
Graph
resource "aws_instance" "myec2" { ami = "ami-082b5a644766e0e6f" instance_type = "t2.micro" } resource "aws_eip" "lb" { instance = aws_instance.myec2.id vpc = true } resource "aws_security_group" "allow_tls" { name = "allow_tls" ingress { description = "TLS from VPC" from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = ["${aws_eip.lb.private_ip}/32"] } }terraform graph > graph.dot yum install graphviz cat graph.dot | dot -Tsvg > graph.svg
plan-file
terraform plan -out=demopath terraform apply demopath
"terraform" Setting
terraform { required_version = "< 0.11" required_providers { aws = "~> 2.0" } } resource "aws_instance" "myec2" { ami = "ami-0b1e534a4ff9019e0" instance_type = "t2.micro" }
large-infra
provider "aws" { region = "ap-southeast-1" access_key = "YOUR-KEY" secret_key = "YOUR-KEY" } module "vpc" { source = "terraform-aws-modules/vpc/aws" name = "my-vpc" cidr = "10.0.0.0/16" azs = ["ap-southeast-1a", "ap-southeast-1b", "ap-southeast-1c"] private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"] public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"] tags = { Terraform = "true" Environment = "dev" } } resource "aws_security_group" "allow_ssh_conn" { name = "allow_ssh_conn" description = "Allow SSH inbound traffic" ingress { description = "SSH into VPC" from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } ingress { description = "HTTP into VPC" from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { description = "Outbound Allowed" from_port = 0 to_port = 65535 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } } resource "aws_instance" "myec2" { ami = "ami-0b1e534a4ff9019e0" instance_type = "t2.micro" key_name = "ec2-key" vpc_security_group_ids = [aws_security_group.allow_ssh_conn.id] }terrafom plan -target=ec2 Setting Refresh as False: terraform plan -refresh=false Setting Refresh along with Target flags terraform plan -refresh=false -target=aws_security_group.allow_ssh_conn
AWS Hardening with Terraform Code
vpc flow logs security hub aws config cloud trial Seperate folders for each services ec2.tf rds.tf sg.tf vpc.tf
Lifecycle-meta-argument-ignore_changes
resource "aws_instance" "myec2" { ami = "ami-0f34c5ae932e6f0e4" instance_type = "t2.micro" tags = { Name = "HelloEarth" } lifecycle { ignore_changes = [tags] # ignore_changes = [tags,instance_type] # ignore_changes = all } }
Lifecycle-meta-argument-create-before-destroy
resource "aws_instance" "myec2" { ami = "ami-0f34c5ae932e6f0e4" instance_type = "t2.micro" tags = { Name = "HelloEarth" } lifecycle { create_before_destroy = true } }
Lifecycle-meta-argument-prevent_destroy
resource "aws_instance" "myec2" { ami = "ami-0f34c5ae932e6f0e4" instance_type = "t2.micro" tags = { Name = "HelloEarth" } lifecycle { prevent_destroy = true } }
for_each
IAM User resource "aws_iam_user" "iam" { for_each = toset( ["user-01","user-02", "user-03"] ) name = each.key }EC2 Instance Example-1 resource "aws_instance" "myec2" { ami = "ami-0cea098ed2ac54925" for_each = { key1 = "t2.micro" key2 = "t2.medium" } instance_type = each.value key_name = each.key tags = { Name = each.value } }EC2 Instance Example-2 variable "instance_config" { type = map(any) default = { instance1 = { instance_type = "t2.micro", ami = "ami-03a6eaae9938c858c" } instance2 = { instance_type = "t2.small", ami = "ami-053b0d53c279acc90" } } } resource "aws_instance" "AmazonAMI" { for_each = var.instance_config instance_type = each.value.instance_type ami = each.value.ami key_name = each.key tags = { Name = each.value.instance_type } }
Provisioners-local-exec
Provisioners is Removed/Deprecated resource "aws_instance" "myec2" { ami = "ami-04e5276ebb8451442" instance_type = "t2.micro" provisioner "local-exec" { command = "echo ${self.private_ip} >> server_ip.txt" } }Scenerio 1 resource "aws_iam_user" "lb" { name = "demoiamuser" provisioner "local-exec" { command = "echo local-exec provisioner is starting" } }Scenerio 2 resource "aws_iam_user" "lb" { name = "demoiamuser" provisioner "local-exec" { command = "echo local-exec provisioner is starting" } provisioner "local-exec" { command = "echo local-exec provisioner is starting for 2nd time" } }
Provisioners-remote-exec
resource "aws_instance" "myec2" { ami = "ami-04e5276ebb8451442" instance_type = "t2.micro" key_name = "terraform-key" vpc_security_group_ids = ["sg-0edf854d7112cfbf4"] connection { type = "ssh" user = "ec2-user" private_key = file("./terraform-key.pem") host = self.public_ip } provisioner "remote-exec" { inline = [ "sudo yum -y install nginx", "sudo systemctl start nginx", ] } }console this command if facing Issue ssh -i terraform-key.pem ec2-user@54.234.184.188 provisioner-remote-exec-types resource "aws_security_group" "allow_ssh" { name = "allow_ssh" description = "Allow SSH inbound traffic" ingress { description = "SSH into VPC" from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { description = "Outbound Allowed" from_port = 0 to_port = 65535 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } } resource "aws_instance" "myec2" { ami = "ami-0b1e534a4ff9019e0" instance_type = "t2.micro" key_name = "ec2-key" vpc_security_group_ids = [aws_security_group.allow_ssh.id] provisioner "remote-exec" { inline = [ "sudo yum -y install nano" ] } provisioner "remote-exec" { when = destroy inline = [ "sudo yum -y remove nano" ] } connection { type = "ssh" user = "ec2-user" private_key = file("./ec2-key.pem") host = self.public_ip } }
Provisioners-failure-behaviour
This will Fail resource "aws_iam_user" "lb" { name = "demo-provisioner-user" provisioner "local-exec" { command = "echo1 This is creation time provisioner" } }on-failure to continue resource "aws_iam_user" "lb" { name = "demo-provisioner-user" provisioner "local-exec" { command = "echo1 This is creation time provisioner" on_failure = continue } }
Provisioners-create-destroy-time-provisioner
resource "aws_iam_user" "lb" { name = "provisioner-user" provisioner "local-exec" { command = "echo This is creation time provisioner" } provisioner "local-exec" { command = "echo This is destroy time provisioner" when = destroy } }Simulating failure to see Tainting of Resource resource "aws_iam_user" "lb" { name = "provisioner-user" provisioner "local-exec" { command = "This is creation time provisioner" } provisioner "local-exec" { command = "echo This is destroy time provisioner" when = destroy } }
Modules
We can centralize terraform resources and call from tf files whenever required. (module "source") Root Module -> module "ec2" Child Module -> source = "./app" Modules Locations 1. Github 2. HTTP URLs 3. S3 Buckets 4. Terraform Registry 5. Local paths module "ec2" { source = "../modules/ec2" } module "ec2" { source = "git::" version = "0.2" }
Modules-local-Output
Modules main.tf resource "aws_instance" "ec2" { ami = var.ami instance_type = local.instance_type key_name = var.key_name monitoring = var.monitoring vpc_security_group_ids = var.vpc_security_group_ids associate_public_ip_address = var.associate_public_ip_address instance_initiated_shutdown_behavior = var.instance_initiated_shutdown_behavior source_dest_check = var.source_dest_check disable_api_termination = var.disable_api_termination } locals { instance_type = "t2.micro" } output "ec2Output" { value = aws_instance.ec2[*] }variables.tf variable "ami" { default = "ami-00fa32593b478ad6e" } variable "key_name" { default = "VKNMatrimony" } variable "monitoring" { default = true } variable "vpc_security_group_ids" { default = ["sg-0577927322bbe2b2a"] } variable "associate_public_ip_address" { default = true } variable "instance_initiated_shutdown_behavior" { default = "stop" } variable "source_dest_check" { default = false } variable "disable_api_termination" { default = true }EC2 myec2.tf module "ec2module" { source = "../modules" associate_public_ip_address = false } resource "aws_eip" "lb" { instance = module.ec2module.ec2Output[0].id domain = "vpc" } output "ModuleOutput" { value = module.ec2module.ec2Output } output "ElasticIPOutput" { value = aws_eip.lb }
Modules-local-Output->ConsoleLog
PS D:\terraform-aws\modules\EC2> terraform apply --auto-approve Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # aws_eip.lb will be created + resource "aws_eip" "lb" { + allocation_id = (known after apply) + arn = (known after apply) + association_id = (known after apply) + carrier_ip = (known after apply) + customer_owned_ip = (known after apply) + domain = "vpc" + id = (known after apply) + instance = (known after apply) + network_border_group = (known after apply) + network_interface = (known after apply) + private_dns = (known after apply) + private_ip = (known after apply) + ptr_record = (known after apply) + public_dns = (known after apply) + public_ip = (known after apply) + public_ipv4_pool = (known after apply) + tags_all = (known after apply) + vpc = (known after apply) } # module.ec2module.aws_instance.ec2 will be created + resource "aws_instance" "ec2" { + ami = "ami-00fa32593b478ad6e" + arn = (known after apply) + associate_public_ip_address = false + availability_zone = (known after apply) + cpu_core_count = (known after apply) + cpu_threads_per_core = (known after apply) + disable_api_stop = (known after apply) + disable_api_termination = true + ebs_optimized = (known after apply) + get_password_data = false + host_id = (known after apply) + host_resource_group_arn = (known after apply) + iam_instance_profile = (known after apply) + id = (known after apply) + instance_initiated_shutdown_behavior = "stop" + instance_lifecycle = (known after apply) + instance_state = (known after apply) + instance_type = "t2.micro" + ipv6_address_count = (known after apply) + ipv6_addresses = (known after apply) + key_name = "VKNMatrimony" + monitoring = true + outpost_arn = (known after apply) + password_data = (known after apply) + placement_group = (known after apply) + placement_partition_number = (known after apply) + primary_network_interface_id = (known after apply) + private_dns = (known after apply) + private_ip = (known after apply) + public_dns = (known after apply) + public_ip = (known after apply) + secondary_private_ips = (known after apply) + security_groups = (known after apply) + source_dest_check = false + spot_instance_request_id = (known after apply) + subnet_id = (known after apply) + tags_all = (known after apply) + tenancy = (known after apply) + user_data = (known after apply) + user_data_base64 = (known after apply) + user_data_replace_on_change = false + vpc_security_group_ids = [ + "sg-0577927322bbe2b2a", ] } Plan: 2 to add, 0 to change, 0 to destroy. Changes to Outputs: + ElasticIPOutput = { + address = null + allocation_id = (known after apply) + arn = (known after apply) + associate_with_private_ip = null + association_id = (known after apply) + carrier_ip = (known after apply) + customer_owned_ip = (known after apply) + customer_owned_ipv4_pool = null + domain = "vpc" + id = (known after apply) + instance = (known after apply) + network_border_group = (known after apply) + network_interface = (known after apply) + private_dns = (known after apply) + private_ip = (known after apply) + ptr_record = (known after apply) + public_dns = (known after apply) + public_ip = (known after apply) + public_ipv4_pool = (known after apply) + tags = null + tags_all = (known after apply) + timeouts = null + vpc = (known after apply) } + ModuleOutput = [ + { + ami = "ami-00fa32593b478ad6e" + arn = (known after apply) + associate_public_ip_address = false + availability_zone = (known after apply) + capacity_reservation_specification = (known after apply) + cpu_core_count = (known after apply) + cpu_options = (known after apply) + cpu_threads_per_core = (known after apply) + credit_specification = [] + disable_api_stop = (known after apply) + disable_api_termination = true + ebs_block_device = (known after apply) + ebs_optimized = (known after apply) + enclave_options = (known after apply) + ephemeral_block_device = (known after apply) + get_password_data = false + hibernation = null + host_id = (known after apply) + host_resource_group_arn = (known after apply) + iam_instance_profile = (known after apply) + id = (known after apply) + instance_initiated_shutdown_behavior = "stop" + instance_lifecycle = (known after apply) + instance_market_options = (known after apply) + instance_state = (known after apply) + instance_type = "t2.micro" + ipv6_address_count = (known after apply) + ipv6_addresses = (known after apply) + key_name = "VKNMatrimony" + launch_template = [] + maintenance_options = (known after apply) + metadata_options = (known after apply) + monitoring = true + network_interface = (known after apply) + outpost_arn = (known after apply) + password_data = (known after apply) + placement_group = (known after apply) + placement_partition_number = (known after apply) + primary_network_interface_id = (known after apply) + private_dns = (known after apply) + private_dns_name_options = (known after apply) + private_ip = (known after apply) + public_dns = (known after apply) + public_ip = (known after apply) + root_block_device = (known after apply) + secondary_private_ips = (known after apply) + security_groups = (known after apply) + source_dest_check = false + spot_instance_request_id = (known after apply) + subnet_id = (known after apply) + tags = null + tags_all = (known after apply) + tenancy = (known after apply) + timeouts = null + user_data = (known after apply) + user_data_base64 = (known after apply) + user_data_replace_on_change = false + volume_tags = null + vpc_security_group_ids = [ + "sg-0577927322bbe2b2a", ] }, ] module.ec2module.aws_instance.ec2: Creating... module.ec2module.aws_instance.ec2: Still creating... [10s elapsed] module.ec2module.aws_instance.ec2: Still creating... [20s elapsed] module.ec2module.aws_instance.ec2: Still creating... [30s elapsed] module.ec2module.aws_instance.ec2: Creation complete after 33s [id=i-0aa1f5c14d0191cda] aws_eip.lb: Creating... aws_eip.lb: Creation complete after 1s [id=eipalloc-018f527379f9048b8] Apply complete! Resources: 2 added, 0 changed, 0 destroyed. Outputs: ElasticIPOutput = { "address" = tostring(null) "allocation_id" = "eipalloc-018f527379f9048b8" "arn" = "arn:aws:ec2:ap-south-1:927437247370:elastic-ip/eipalloc-018f527379f9048b8" "associate_with_private_ip" = tostring(null) "association_id" = "eipassoc-042cab9dc8771a592" "carrier_ip" = "" "customer_owned_ip" = "" "customer_owned_ipv4_pool" = "" "domain" = "vpc" "id" = "eipalloc-018f527379f9048b8" "instance" = "i-0aa1f5c14d0191cda" "network_border_group" = "ap-south-1" "network_interface" = "eni-0a6d24ba8202461d9" "private_dns" = "ip-172-31-34-194.ap-south-1.compute.internal" "private_ip" = "172.31.34.194" "ptr_record" = "" "public_dns" = "ec2-13-200-109-120.ap-south-1.compute.amazonaws.com" "public_ip" = "13.200.109.120" "public_ipv4_pool" = "amazon" "tags" = tomap(null) /* of string */ "tags_all" = tomap({}) "timeouts" = null /* object */ "vpc" = true } ModuleOutput = [ { "ami" = "ami-00fa32593b478ad6e" "arn" = "arn:aws:ec2:ap-south-1:927437247370:instance/i-0aa1f5c14d0191cda" "associate_public_ip_address" = true "availability_zone" = "ap-south-1a" "capacity_reservation_specification" = tolist([ { "capacity_reservation_preference" = "open" "capacity_reservation_target" = tolist([]) }, ]) "cpu_core_count" = 1 "cpu_options" = tolist([ { "amd_sev_snp" = "" "core_count" = 1 "threads_per_core" = 1 }, ]) "cpu_threads_per_core" = 1 "credit_specification" = tolist([ { "cpu_credits" = "standard" }, ]) "disable_api_stop" = false "disable_api_termination" = true "ebs_block_device" = toset([]) "ebs_optimized" = false "enclave_options" = tolist([ { "enabled" = false }, ]) "ephemeral_block_device" = toset([]) "get_password_data" = false "hibernation" = false "host_id" = "" "host_resource_group_arn" = tostring(null) "iam_instance_profile" = "" "id" = "i-0aa1f5c14d0191cda" "instance_initiated_shutdown_behavior" = "stop" "instance_lifecycle" = "" "instance_market_options" = tolist([]) "instance_state" = "running" "instance_type" = "t2.micro" "ipv6_address_count" = 0 "ipv6_addresses" = tolist([]) "key_name" = "VKNMatrimony" "launch_template" = tolist([]) "maintenance_options" = tolist([ { "auto_recovery" = "default" }, ]) "metadata_options" = tolist([ { "http_endpoint" = "enabled" "http_protocol_ipv6" = "disabled" "http_put_response_hop_limit" = 2 "http_tokens" = "required" "instance_metadata_tags" = "disabled" }, ]) "monitoring" = true "network_interface" = toset([]) "outpost_arn" = "" "password_data" = "" "placement_group" = "" "placement_partition_number" = 0 "primary_network_interface_id" = "eni-0a6d24ba8202461d9" "private_dns" = "ip-172-31-34-194.ap-south-1.compute.internal" "private_dns_name_options" = tolist([ { "enable_resource_name_dns_a_record" = false "enable_resource_name_dns_aaaa_record" = false "hostname_type" = "ip-name" }, ]) "private_ip" = "172.31.34.194" "public_dns" = "ec2-13-233-125-208.ap-south-1.compute.amazonaws.com" "public_ip" = "13.233.125.208" "root_block_device" = tolist([ { "delete_on_termination" = true "device_name" = "/dev/xvda" "encrypted" = false "iops" = 3000 "kms_key_id" = "" "tags" = tomap({}) "tags_all" = tomap({}) "throughput" = 125 "volume_id" = "vol-0ebd88b75504eafc3" "volume_size" = 8 "volume_type" = "gp3" }, ]) "secondary_private_ips" = toset([]) "security_groups" = toset([ "vijay-terraform-firewall", ]) "source_dest_check" = false "spot_instance_request_id" = "" "subnet_id" = "subnet-0336a23d310337a48" "tags" = tomap(null) /* of string */ "tags_all" = tomap({}) "tenancy" = "default" "timeouts" = null /* object */ "user_data" = tostring(null) "user_data_base64" = tostring(null) "user_data_replace_on_change" = false "volume_tags" = tomap(null) /* of string */ "vpc_security_group_ids" = toset([ "sg-0577927322bbe2b2a", ]) }, ]
Workspace
Workspaces allow multiple state files of a single configuration resource "aws_instance" "myec2" { ami = "ami-0fbeaa7bf4665bd6f" instance_type = lookup(var.instance_type, terraform.workspace) #lookup(map, key, default) } variable "instance_type" { type = map(any) default = { default = "t2.medium" dev = "t2.micro" prd = "t2.large" } }
Workspace-Output
PS D:\terraform-aws\SecurityGroup\workspace> terraform workspace Usage: terraform [global options] workspace new, list, show, select and delete Terraform workspaces. Subcommands: delete Delete a workspace list List Workspaces new Create a new workspace select Select a workspace show Show the name of the current workspace PS D:\terraform-aws\SecurityGroup\workspace> terraform workspace show default PS D:\terraform-aws\SecurityGroup\workspace> terraform workspace list * default PS D:\terraform-aws\SecurityGroup\workspace> terraform workspace new dev Created and switched to workspace "dev"! PS D:\terraform-aws\SecurityGroup\workspace> terraform workspace new prd Created and switched to workspace "prd"! PS D:\terraform-aws\SecurityGroup\workspace> terraform workspace list default dev * prd PS D:\terraform-aws\SecurityGroup\vijay> terraform workspace select dev Switched to workspace "dev". PS D:\terraform-aws\SecurityGroup\vijay> terraform plan # aws_instance.myec2 will be created + resource "aws_instance" "myec2" { + ami = "ami-0fbeaa7bf4665bd6f" + instance_type = "t2.micro" PS D:\terraform-aws\SecurityGroup\vijay> terraform workspace select prd Switched to workspace "prd". PS D:\terraform-aws\SecurityGroup\vijay> terraform plan # aws_instance.myec2 will be created + resource "aws_instance" "myec2" { + ami = "ami-0fbeaa7bf4665bd6f" + instance_type = "t2.large".tfstate file will be saved in folder terraform.tfstate.d and inside dev folder .tfstate file will be saved
S3 Backend
backend.tf terraform { backend "s3" { bucket = "thakishore-bitbucket-terraform" key = "bitbucket-terraform.tfstate" region = "ap-south-1" } }main.tf resource "aws_eip" "lb" { domain = "vpc" }
State File Locking
sleep.tf resource "time_sleep" "wait_300_seconds" { create_duration = "300s" }If Simuntaneous users uses terraform commands new user will get shown error
StateFileLockDynamo
terraform { backend "s3" { bucket = "thakishore-terraform-backend" dynamodb_table = "terraform-state-lock-dynamo" key = "network/demo.tfstate" region = "ap-south-1" } } resource "time_sleep" "wait_150_seconds" { create_duration = "150s" }terraform apply -replace="time_sleep.wait_150_seconds demo.tfstate file will be removed if lock released
State Management
terraform state list -> List resources within terraform state file terraform state pull -> Manually download and output the state from remote stateList resources within terraform state file terraform state push -> Manually upload a local state file to remote state terraform state rm time_sleep.wait_120_seconds -> Remove items from the terraform state terraform state show time_sleep.wait_120_seconds -> Show the attributes of a single resource in the state terraform state mv time_sleep.wait_150_seconds time_sleep.wait_120_seconds -> Moves item with terraform state
Remote State
Network Team backend.tf terraform { backend "s3" { bucket = "thakishore-terraform-backend" key = "network/eip.tfstate" region = "ap-south-1" } }eip.tf resource "aws_eip" "lb" { vpc = true } output "eip_addr" { value = aws_eip.lb.public_ip }Security Team remote-state.tf data "terraform_remote_state" "eip" { backend = "s3" config = { bucket = "thakishore-terraform-backend" key = "network/eip.tfstate" region = "us-east-1" } }sg.tf resource "aws_security_group" "allow_tls" { name = "allow_tls" description = "Allow TLS inbound traffic" ingress { description = "TLS from VPC" from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = ["${data.terraform_remote_state.eip.outputs.eip_addr}/32"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] ipv6_cidr_blocks = ["::/0"] } }
Import
Import Block import { to = aws_security_group.New-SG id = "sg-005168b9ac9dd37b7" } resource "aws_security_group" "New-SG" { name = "New-SG" # Initially, this can be empty or have some basic information # Terraform will update the state after import }Import Command terraform import aws_security_group.New-SG sg-005168b9ac9dd37b7Import Plan Commands terraform plan -generate-config-out terraform plan -generate-config-out=generated.tf terraform plan -generate-config-out=mysg.tf -chdir D:\AWS Triad & Terraform\importTerraform Import available from Terraform 1.5 version onwards not before
Multiple-Providers
providers.tf provider "aws" { region = "us-west-1" } provider "aws" { alias = "aws02" region = "ap-south-1" profile = "account02" }eip.tf resource "aws_eip" "myeip" { vpc = "true" } resource "aws_eip" "myeip01" { domain = "vpc" provider = "aws.aws02" }.aws From cli aws s3 ls --profile account02
[default] aws_access_key_id = xxxxxxxxxxxxxxxxx aws_secret_access_key = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx [account02] aws_access_key_id = xxxxxxxxxxxxxxxxx aws_secret_access_key = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Sensitive Parameter
locals { db_password = { admin = "password" } } output "db_password" { value = local.db_password sensitive = true }
Vault
provider "vault" { address = "http://127.0.0.1:8200" } data "vault_generic_secret" "demo" { path = "secret/db_creds" } output "vault_secrets" { value = data.vault_generic_secret.demo.data_json sensitive = "true" }
Dependency-lock
terraform { required_providers { aws = { source = "hashicorp/aws" version = "4.60" } } } # Configure the AWS Provider provider "aws" { region = "us-east-1" } resource "aws_instance" "web" { ami = ami-123 instance_type = "t2.micro" }
Heredoc Syntax
EOF - End of File EOT - End of Text END user_data = <<-EOF$newHostname = "New-Hostname" Rename-Computer -NewName $newHostname -Force -PassThru Restart-Computer -Force EOF
Terraform-Older-Version
terraform older version releases https://releases.hashicorp.com/terraform provider "aws" { version = "~> 2.54" region = "ap-south-1" access_key = "xxxxxx" secret_key = "xxxxxxxxxxxxxxxxx" } provider "digitalocean" {} terraform { required_version = "0.12.31" } resource "aws_eip" "eip" { vpc = true }
Example-1
data "aws_iam_users" "users" {} data "aws_caller_identity" "current" {} output "accountid" { value = data.aws_caller_identity.current.account_id } resource "aws_iam_user" "user_name" { name = "admin-user-${data.aws_caller_identity.current.account_id}" path = "/system/" } output "user_names" { value = data.aws_iam_users.users.names } output "total_users" { value = length(data.aws_iam_users.users.names) }Outputs Changes to Outputs: + accountid = "590184002190" + total_users = 1 + user_names = [ + "terraform", ]
STS Pending