Skip to main content

Terraform Resources va Data Sources

Resource nima?

Resource - bu Terraform'da yaratilishi va boshqarilishi kerak bo'lgan infratuzilma komponenti. Har bir resource ma'lum bir provider'ga tegishli va real dunyo obyektini (server, database, network va h.k.) ifodalaydi.

Resource'ning roli:

Resource'lar Terraform'ning asosiy building block'lari hisoblanadi. Ular:

  1. Lifecycle'ni boshqaradi: Create, Update, Delete operatsiyalari
  2. State'ni saqlaydi: Resource'ning joriy holatini kuzatib boradi
  3. Dependencies'ni aniqlaydi: Qaysi resource'lar birinchi yaratilishi kerakligini biladi
  4. Idempotent: Bir xil kodni qayta ishlatish bir xil natija beradi

Resource sintaksisi:

resource "<RESOURCE_TYPE>" "<RESOURCE_NAME>" {
# Resource konfiguratsiyasi
<argument_name> = <argument_value>
...
}

Tushuntirish:

  • resource - kalit so'z
  • <RESOURCE_TYPE> - provider va resource turi (masalan: aws_instance)
  • <RESOURCE_NAME> - siz bergan nom (masalan: web_server)
  • Blok ichida - resource argumentlari

Oddiy misol:

# EC2 instance yaratish
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"

tags = {
Name = "WebServer"
}
}

Bu kod nima qiladi:

  1. AWS'da EC2 instance yaratadi
  2. Belgilangan AMI'dan foydalanadi
  3. t2.micro instance type'ini tanlaydi
  4. Tag qo'shadi

Resource identifier:

Resource'ga murojaat qilish uchun identifier ishlatiladi:

<resource_type>.<resource_name>

# Misol:
aws_instance.web

Resource attribute'lariga murojaat:

<resource_type>.<resource_name>.<attribute>

# Misol:
aws_instance.web.id
aws_instance.web.public_ip
aws_instance.web.private_ip

Resource lifecycle

Terraform resource'lar uchun lifecycle'ni qanday boshqarishini tushunish muhim.

1. Yaratish (Create)

terraform apply

Terraform:

  1. Plan yaratadi
  2. Provider API'ga CREATE so'rovini yuboradi
  3. Resource yaratilishini kutadi
  4. State'ga yozadi
resource "aws_s3_bucket" "data" {
bucket = "my-data-bucket"

tags = {
Name = "Data Bucket"
}
}

2. O'qish (Read)

terraform refresh

Terraform:

  1. Provider API'dan joriy holatni o'qiydi
  2. State'ni yangilaydi
  3. Drift detection (farqlarni aniqlash)

3. Yangilash (Update)

Konfiguratsiyani o'zgartirsangiz:

resource "aws_s3_bucket" "data" {
bucket = "my-data-bucket"

tags = {
Name = "Data Bucket"
Environment = "Production" # Yangi tag
}
}
terraform apply

Terraform:

  1. O'zgarishlarni aniqlaydi
  2. UPDATE so'rovini yuboradi
  3. State'ni yangilaydi

4. O'chirish (Delete)

terraform destroy

Yoki resource'ni konfiguratsiyadan olib tashlash:

# Resource o'chirildi konfiguratsiyadan
terraform apply

Terraform:

  1. DELETE so'rovini yuboradi
  2. Resource yo'qotilishini kutadi
  3. State'dan olib tashlaydi

Resource lifecycle diagrammasi:

┌─────────────────────────────────────────┐
│ Konfiguratsiya (.tf fayllar) │
└──────────────┬──────────────────────────┘


┌─────────────────────────────────────────┐
│ terraform plan │
│ - Joriy state o'qish │
│ - Kerakli state hisoblash │
│ - Farqlarni aniqlash │
└──────────────┬──────────────────────────┘


┌─────────────────────────────────────────┐
│ Execution Plan │
│ + create (yaratish) │
│ ~ update (yangilash) │
│ - destroy (o'chirish) │
└──────────────┬──────────────────────────┘


┌─────────────────────────────────────────┐
│ terraform apply │
│ - API so'rovlar │
│ - Resource operatsiyalari │
│ - State yangilash │
└──────────────┬──────────────────────────┘


┌─────────────────────────────────────────┐
│ Yaratilgan Infrastructure │
└─────────────────────────────────────────┘

Resource meta-arguments

Meta-arguments - bu barcha resource'larga qo'llaniladigan maxsus argumentlar. Ular resource behavior'ini boshqaradi.

1. depends_on - Bog'liqliklarni belgilash

Ba'zan Terraform avtomatik ravishda bog'liqliklarni aniqlay olmaydi. depends_on orqali qo'lda belgilash mumkin.

# S3 bucket yaratish
resource "aws_s3_bucket" "logs" {
bucket = "my-app-logs"
}

# S3 bucket policy
resource "aws_s3_bucket_policy" "logs_policy" {
bucket = aws_s3_bucket.logs.id

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = "*"
Action = "s3:GetObject"
Resource = "${aws_s3_bucket.logs.arn}/*"
}
]
})
}

# EC2 instance - bucket yaratilgandan keyin
resource "aws_instance" "web" {
ami = "ami-123456"
instance_type = "t2.micro"

# Explicit dependency
depends_on = [
aws_s3_bucket.logs,
aws_s3_bucket_policy.logs_policy
]
}

Qachon ishlatish kerak:

  • Implicit dependency yo'q, lekin tartib muhim
  • Provider API limitations
  • External systems bilan integratsiya

Best practice:

  • Faqat zarur bo'lganda ishlating
  • Ko'pincha Terraform avtomatik aniqlaydi

2. count - Bir nechta instance yaratish

count meta-argument bir xil resource'dan bir nechta nusxa yaratish imkonini beradi.

# 3 ta server yaratish
resource "aws_instance" "web" {
count = 3

ami = "ami-123456"
instance_type = "t2.micro"

tags = {
Name = "web-server-${count.index}"
}
}

count.index - 0 dan boshlanadigan index:

  • Birinchi instance: count.index = 0
  • Ikkinchi instance: count.index = 1
  • Uchinchi instance: count.index = 2

Count bilan shartli yaratish:

variable "create_instance" {
type = bool
default = true
}

resource "aws_instance" "optional" {
# true bo'lsa 1 ta, false bo'lsa 0 ta yaratadi
count = var.create_instance ? 1 : 0

ami = "ami-123456"
instance_type = "t2.micro"
}

Count bilan list:

variable "availability_zones" {
default = ["us-east-1a", "us-east-1b", "us-east-1c"]
}

resource "aws_subnet" "public" {
count = length(var.availability_zones)

vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(aws_vpc.main.cidr_block, 8, count.index)
availability_zone = var.availability_zones[count.index]

tags = {
Name = "public-subnet-${count.index + 1}"
}
}

Count natijalariga murojaat:

# Birinchi instance
aws_instance.web[0]

# Ikkinchi instance
aws_instance.web[1]

# Barcha instance ID'lar
aws_instance.web[*].id

# Output
output "instance_ids" {
value = aws_instance.web[*].id
}

output "first_instance_ip" {
value = aws_instance.web[0].public_ip
}

Count cheklovi: Count'dan foydalanganda element o'chirsangiz, yangi index'lar o'zgaradi. Bu Terraform'ni yangi resource'lar yaratishga majbur qiladi.

3. for_each - Map yoki set ustida iteratsiya

for_each countga qaraganda ko'proq flexibility beradi. Map yoki set ustida ishlaydi.

Set bilan:

variable "user_names" {
type = set(string)
default = ["alice", "bob", "charlie"]
}

resource "aws_iam_user" "users" {
for_each = var.user_names
name = each.key # alice, bob, charlie
}

Map bilan:

variable "instances" {
type = map(object({
instance_type = string
ami = string
}))

default = {
web = {
instance_type = "t2.micro"
ami = "ami-123456"
}
api = {
instance_type = "t2.small"
ami = "ami-789012"
}
db = {
instance_type = "t2.medium"
ami = "ami-345678"
}
}
}

resource "aws_instance" "servers" {
for_each = var.instances

ami = each.value.ami
instance_type = each.value.instance_type

tags = {
Name = "${each.key}-server"
Role = each.key
}
}

each obyekti:

  • each.key - kalit (map'da key, set'da element)
  • each.value - qiymat (faqat map'da)

For_each natijalariga murojaat:

# Muayyan instance
aws_instance.servers["web"]
aws_instance.servers["api"]

# Attribute'ga murojaat
aws_instance.servers["web"].id
aws_instance.servers["api"].public_ip

# Output
output "instance_details" {
value = {
for key, instance in aws_instance.servers :
key => {
id = instance.id
public_ip = instance.public_ip
}
}
}

Count vs for_each:

Xususiyatcountfor_each
InputNumberSet yoki Map
IndexRaqam (0, 1, 2)Kalit
Element o'chirishIndex o'zgaradiIndex barqaror
FlexibilityPastYuqori
Qachon ishlatishOddiy holatlarMurakkab holatlar

Best practice: Ko'proq for_each ishlatish tavsiya etiladi.

4. provider - Muayyan provider'dan foydalanish

Bir nechta provider alias'lari bo'lganda, resource qaysi biridan foydalanishini belgilash mumkin.

# US East provider
provider "aws" {
region = "us-east-1"
}

# US West provider
provider "aws" {
alias = "west"
region = "us-west-2"
}

# EU provider
provider "aws" {
alias = "eu"
region = "eu-west-1"
}

# Default provider (us-east-1)
resource "aws_instance" "east_server" {
ami = "ami-123456"
instance_type = "t2.micro"
}

# US West provider
resource "aws_instance" "west_server" {
provider = aws.west

ami = "ami-789012"
instance_type = "t2.micro"
}

# EU provider
resource "aws_s3_bucket" "eu_bucket" {
provider = aws.eu
bucket = "my-eu-bucket"
}

5. lifecycle - Resource lifecycle'ni boshqarish

lifecycle bloki resource'ning create, update, delete jarayonlarini boshqarish imkonini beradi.

create_before_destroy:

Yangi resource yaratib, keyin eskisini o'chiradi. Bu zero-downtime deployment uchun juda foydali.

resource "aws_instance" "web" {
ami = "ami-123456"
instance_type = "t2.micro"

lifecycle {
create_before_destroy = true
}
}

Ishlash tartibi:

  1. Yangi instance yaratiladi
  2. Yangi instance tayyor bo'lgandan keyin
  3. Eski instance o'chiriladi

prevent_destroy:

Resource'ni tasodifan o'chirishdan saqlaydi. Production environment'da juda muhim.

resource "aws_db_instance" "production" {
identifier = "prod-database"
instance_class = "db.t3.micro"

lifecycle {
prevent_destroy = true
}
}

Agar terraform destroy yoki resource'ni konfiguratsiyadan o'chirmoqchi bo'lsangiz, xato beradi:

Error: Instance cannot be destroyed

ignore_changes:

Ma'lum attribute'lardagi o'zgarishlarni e'tiborsiz qoldiradi. Bu external systems tomonidan o'zgartirilgan qiymatlar uchun foydali.

resource "aws_instance" "web" {
ami = "ami-123456"
instance_type = "t2.micro"

tags = {
Name = "WebServer"
}

lifecycle {
# Tags'ni ignore qilish (boshqa tool'lar o'zgartirishi mumkin)
ignore_changes = [tags]
}
}

Barcha o'zgarishlarni ignore qilish:

resource "aws_instance" "imported" {
ami = "ami-123456"
instance_type = "t2.micro"

lifecycle {
ignore_changes = all
}
}

replace_triggered_by:

Ma'lum resource o'zgarganda bu resource'ni replace qiladi:

resource "aws_security_group" "web" {
name = "web-sg"
# ...
}

resource "aws_instance" "web" {
ami = "ami-123456"
instance_type = "t2.micro"

vpc_security_group_ids = [aws_security_group.web.id]

lifecycle {
replace_triggered_by = [
aws_security_group.web
]
}
}

precondition va postcondition:

Resource yaratishdan oldin yoki keyin shartlarni tekshiradi (Terraform 1.2+):

resource "aws_instance" "web" {
ami = var.ami_id
instance_type = var.instance_type

lifecycle {
# Yaratishdan oldin tekshirish
precondition {
condition = var.instance_type != ""
error_message = "Instance type cannot be empty"
}

precondition {
condition = can(regex("^ami-", var.ami_id))
error_message = "AMI ID must start with 'ami-'"
}

# Yaratilgandan keyin tekshirish
postcondition {
condition = self.public_ip != ""
error_message = "Instance must have a public IP address"
}

postcondition {
condition = self.instance_state == "running"
error_message = "Instance must be in running state"
}
}
}

Lifecycle to'liq misol:

resource "aws_instance" "critical_server" {
ami = var.ami_id
instance_type = var.instance_type

user_data = file("init-script.sh")

tags = {
Name = "CriticalServer"
Environment = "Production"
}

lifecycle {
# Yangi yaratib, keyin eskisini o'chirish
create_before_destroy = true

# Tasodifan o'chirishdan himoya
prevent_destroy = true

# User data o'zgarishlarini ignore qilish
# (instance rebuild talab qiladi)
ignore_changes = [
user_data,
tags["LastModified"]
]

# Precondition
precondition {
condition = var.instance_type != ""
error_message = "Instance type must be specified"
}

# Postcondition
postcondition {
condition = self.instance_state == "running"
error_message = "Instance must be running"
}
}
}

6. provisioner - Resource yaratilgandan keyin amallar

Provisioner'lar resource yaratilgandan yoki o'chirilgandan keyin qo'shimcha amallarni bajarish imkonini beradi.

⚠️ Ogohlantirish: Provisioner'lar "last resort" hisoblanadi. Ko'proq Packer, cloud-init, yoki config management tool'larni (Ansible) ishlatish tavsiya etiladi.

local-exec provisioner:

Local mashinada buyruq bajaradi:

resource "aws_instance" "web" {
ami = "ami-123456"
instance_type = "t2.micro"

provisioner "local-exec" {
command = "echo ${self.private_ip} >> private_ips.txt"
}

provisioner "local-exec" {
command = "ansible-playbook -i '${self.public_ip},' playbook.yml"
}
}

remote-exec provisioner:

Remote serverda buyruq bajaradi:

resource "aws_instance" "web" {
ami = "ami-123456"
instance_type = "t2.micro"
key_name = aws_key_pair.deployer.key_name

provisioner "remote-exec" {
inline = [
"sudo apt-get update",
"sudo apt-get install -y nginx",
"sudo systemctl start nginx",
"sudo systemctl enable nginx"
]

connection {
type = "ssh"
user = "ubuntu"
private_key = file("~/.ssh/id_rsa")
host = self.public_ip
}
}
}

file provisioner:

Fayllarni copy qiladi:

resource "aws_instance" "web" {
ami = "ami-123456"
instance_type = "t2.micro"

provisioner "file" {
source = "conf/myapp.conf"
destination = "/etc/myapp.conf"

connection {
type = "ssh"
user = "ubuntu"
private_key = file("~/.ssh/id_rsa")
host = self.public_ip
}
}

provisioner "file" {
content = templatefile("script.tpl", { var = "value" })
destination = "/tmp/script.sh"

connection {
type = "ssh"
user = "ubuntu"
host = self.public_ip
}
}
}

Provisioner when:

Provisioner qachon ishlashini belgilash:

resource "aws_instance" "web" {
ami = "ami-123456"
instance_type = "t2.micro"

# Resource yaratilganda (default)
provisioner "local-exec" {
when = create
command = "echo 'Instance created'"
}

# Resource o'chirilganda
provisioner "local-exec" {
when = destroy
command = "echo 'Instance destroyed'"
}
}

Provisioner on_failure:

Xatolik yuz berganda nima qilish:

resource "aws_instance" "web" {
ami = "ami-123456"
instance_type = "t2.micro"

provisioner "remote-exec" {
# Xatolikda davom etish
on_failure = continue

inline = [
"sudo apt-get update",
"sudo apt-get install -y optional-package"
]
}

provisioner "remote-exec" {
# Xatolikda to'xtatish (default)
on_failure = fail

inline = [
"sudo apt-get install -y required-package"
]
}
}

Data Sources

Data Source - bu mavjud infratuzilmadan ma'lumot o'qish uchun ishlatiladi. Resource yaratmaydi, faqat ma'lumot oladi va boshqa resource'larda ishlatish mumkin.

Data Source vs Resource:

XususiyatResourceData Source
Yaratadi✅ Ha❌ Yo'q
O'qiydi✅ Ha✅ Ha
O'zgartiradi✅ Ha❌ Yo'q
O'chiradi✅ Ha❌ Yo'q
State'da✅ Ha✅ Ha (read-only)

Data Source sintaksisi:

data "<DATA_TYPE>" "<NAME>" {
# Filter va selector'lar
<argument> = <value>
}

AWS AMI Data Source:

Mavjud AMI'lardan birini topish:

# Eng oxirgi Ubuntu AMI
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical

filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
}

filter {
name = "virtualization-type"
values = ["hvm"]
}
}

# Data source'dan foydalanish
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = "t2.micro"

tags = {
Name = "WebServer"
AMI = data.aws_ami.ubuntu.name
Created = data.aws_ami.ubuntu.creation_date
}
}

# Output
output "ubuntu_ami_id" {
value = data.aws_ami.ubuntu.id
}