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:
- Lifecycle'ni boshqaradi: Create, Update, Delete operatsiyalari
- State'ni saqlaydi: Resource'ning joriy holatini kuzatib boradi
- Dependencies'ni aniqlaydi: Qaysi resource'lar birinchi yaratilishi kerakligini biladi
- 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:
- AWS'da EC2 instance yaratadi
- Belgilangan AMI'dan foydalanadi
- t2.micro instance type'ini tanlaydi
- 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:
- Plan yaratadi
- Provider API'ga CREATE so'rovini yuboradi
- Resource yaratilishini kutadi
- State'ga yozadi
resource "aws_s3_bucket" "data" {
bucket = "my-data-bucket"
tags = {
Name = "Data Bucket"
}
}
2. O'qish (Read)
terraform refresh
Terraform:
- Provider API'dan joriy holatni o'qiydi
- State'ni yangilaydi
- 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:
- O'zgarishlarni aniqlaydi
- UPDATE so'rovini yuboradi
- State'ni yangilaydi
4. O'chirish (Delete)
terraform destroy
Yoki resource'ni konfiguratsiyadan olib tashlash:
# Resource o'chirildi konfiguratsiyadan
terraform apply
Terraform:
- DELETE so'rovini yuboradi
- Resource yo'qotilishini kutadi
- 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:
| Xususiyat | count | for_each |
|---|---|---|
| Input | Number | Set yoki Map |
| Index | Raqam (0, 1, 2) | Kalit |
| Element o'chirish | Index o'zgaradi | Index barqaror |
| Flexibility | Past | Yuqori |
| Qachon ishlatish | Oddiy holatlar | Murakkab 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:
- Yangi instance yaratiladi
- Yangi instance tayyor bo'lgandan keyin
- 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:
| Xususiyat | Resource | Data 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
}