HCL
HCL (HashiCorp Configuration Language) nima?
HCL - bu HashiCorp tomonidan yaratilgan deklarativ konfiguratsiya tili. U Terraform'ning asosiy tili bo'lib, infratuzilmani kod orqali tasvirlash uchun mo'ljallangan. HCL ning asosiy maqsadi - texnik bo'lmagan xodimlar ham tushunishi mumkin bo'lgan, lekin kompyuter uchun ham aniq bo'lgan konfiguratsiya yozish imkonini berish.
Nima uchun HCL yaratilgan?
HashiCorp JSON yoki YAML kabi mavjud tillardan foydalanishi mumkin edi, lekin ular Terraform'ning ehtiyojlariga to'liq mos kelmadi:
JSON muammolari:
- Kommentariya yozib bo'lmaydi
- Takrorlanish ko'p (trailing commas, quotes)
- Odam o'qishi qiyin (katta fayllarda)
YAML muammolari:
- Indentation'ga juda sezgir
- Xatolarni topish qiyin
- Murakkab strukturalar chalkashtiradi
HCL yechimi:
- Odam o'qishi oson sintaksis
- Kommentariyalar
- Aniq xato xabarlari
- JSON bilan konvertatsiya qilish mumkin
HCL ning asosiy xususiyatlari:
- Deklarativ yondashuv: Siz "nima" kerakligini aytasiz, "qanday" qilishni emas
- Odam o'qishi oson: Oddiy va tushunarli sintaksis
- JSON bilan mos: HCL ↔ JSON konvertatsiya qilish mumkin
- Kommentariyalar: Kodni tushuntirish imkoniyati
- Interpolatsiya: O'zgaruvchilar va ifodalardan foydalanish
- Funksiyalar: 100+ built-in funksiyalar
- Type system: Kuchli type checking
- Idempotent: Bir xil kodni qayta ishlatish bir xil natija beradi
HCL vs JSON taqqoslash
Bir xil konfiguratsiyani ikki xil formatda ko'raylik:
HCL versiyasi (oson o'qiladi):
# EC2 instance yaratish
resource "aws_instance" "web" {
ami = "ami-123456"
instance_type = "t2.micro"
tags = {
Name = "WebServer"
Environment = "Production"
}
}
JSON versiyasi (murakkab):
{
"resource": {
"aws_instance": {
"web": {
"ami": "ami-123456",
"instance_type": "t2.micro",
"tags": {
"Name": "WebServer",
"Environment": "Production"
}
}
}
}
}
Ko'rib turganingizdek, HCL ancha ixcham va o'qish uchun oson.
HCL fayllar
Terraform loyihasida HCL fayllar .tf extension bilan saqlanadi:
my-project/
├── main.tf # Asosiy resurslar
├── variables.tf # Input variables
├── outputs.tf # Output values
├── providers.tf # Provider konfiguratsiyalari
└── versions.tf # Terraform va provider versiyalari
Muhim eslatma: Terraform bitta katalogdagi barcha .tf fayllarni bitta konfiguratsiya sifatida o'qiydi. Shuning uchun fayllarni qanday nomlashingiz muhim emas, lekin best practice uchun mantiqiy ravishda ajratish yaxshi.
Bloklar (Blocks)
Blok - HCL ning asosiy tuzilmasi.
Blok sintaksisi:
<BLOCK_TYPE> "<BLOCK_LABEL>" "<BLOCK_LABEL>" {
# Blok tanasi
<IDENTIFIER> = <EXPRESSION>
}
Asosiy blok turlari:
1. terraform bloki
terraform {
# Terraform versiyasi
required_version = ">= 1.0"
# Provider'lar
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
}
# Backend
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
}
}
2. provider bloki
# AWS Provider
provider "aws" {
region = var.aws_region
access_key = var.aws_access_key
secret_key = var.aws_secret_key
default_tags {
tags = {
Environment = var.environment
ManagedBy = "Terraform"
}
}
}
# Multi-region setup
provider "aws" {
alias = "west"
region = "us-west-2"
}
3. resource bloki
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
vpc_security_group_ids = [aws_security_group.web.id]
subnet_id = aws_subnet.public[0].id
user_data = <<-EOF
#!/bin/bash
apt-get update
apt-get install -y nginx
EOF
tags = {
Name = "web-server"
Environment = var.environment
}
lifecycle {
create_before_destroy = true
}
}
4. data bloki
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
# Data source ishlatish
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
}
5. variable bloki
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t2.micro"
validation {
condition = contains(["t2.micro", "t2.small", "t2.medium"], var.instance_type)
error_message = "Instance type must be t2.micro, t2.small, or t2.medium"
}
}
variable "instance_count" {
description = "Number of instances"
type = number
default = 2
validation {
condition = var.instance_count > 0 && var.instance_count <= 10
error_message = "Instance count must be between 1 and 10"
}
}
6. output bloki
output "instance_id" {
description = "EC2 instance ID"
value = aws_instance.web.id
}
output "instance_public_ip" {
description = "Public IP address"
value = aws_instance.web.public_ip
sensitive = false
}
output "connection_string" {
description = "Database connection string"
value = "postgresql://${aws_db_instance.main.username}@${aws_db_instance.main.endpoint}"
sensitive = true
}
7. locals bloki
locals {
# Oddiy qiymatlar
environment = "production"
region = "us-east-1"
# Hisoblangan qiymatlar
common_tags = {
Environment = local.environment
ManagedBy = "Terraform"
Project = var.project_name
Region = local.region
}
# Shartli qiymatlar
instance_type = local.environment == "prod" ? "t3.large" : "t2.micro"
# List transformatsiyalari
availability_zones = [
"${local.region}a",
"${local.region}b",
"${local.region}c"
]
# Map transformatsiyalari
subnet_configs = {
for idx, az in local.availability_zones :
"subnet-${idx}" => {
cidr_block = cidrsubnet("10.0.0.0/16", 8, idx)
availability_zone = az
}
}
}
8. module bloki
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "3.0.0"
name = "${var.project_name}-vpc"
cidr = "10.0.0.0/16"
azs = ["us-east-1a", "us-east-1b", "us-east-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"]
enable_nat_gateway = true
enable_vpn_gateway = false
tags = local.common_tags
}
# Module output'laridan foydalanish
resource "aws_instance" "web" {
subnet_id = module.vpc.public_subnets[0]
}
Kommentariyalar
# Bu bitta qatorli kommentariya (hash style)
// Bu ham bitta qatorli kommentariya (C++ style)
/* Bu ko'p qatorli
kommentariya bo'lib,
bir necha qatorni
egallaydi */
resource "aws_instance" "web" {
ami = "ami-123456" # Inline kommentariya
instance_type = "t2.micro" // Bu ham inline
/* Multi-line comment
ichida ham ishlaydi */
tags = {
Name = "web-server"
}
}
Ma'lumot turlari (Data Types)
Primitive Types (Oddiy turlar)
1. string (Matn)
variable "instance_name" {
type = string
default = "web-server"
}
# String misollari
locals {
name = "web-server"
region = "us-east-1"
ami = "ami-0c55b159cbfafe1f0"
# Bo'sh string
empty = ""
# Ko'p qatorli string (Heredoc)
script = <<-EOF
#!/bin/bash
echo "Hello World"
apt-get update
EOF
# String interpolatsiya
full_name = "${var.first_name} ${var.last_name}"
message = "Instance count: ${var.count}"
}
2. number (Raqam)
variable "instance_count" {
type = number
default = 3
}
variable "disk_size" {
type = number
default = 100
}
locals {
# Integer
port = 80
count = 5
# Float
price = 9.99
percent = 0.15
# Hisoblashlar
total_memory = var.instance_count * 1024
disk_total = var.disk_size * var.instance_count
}
3. bool (Mantiqiy)
variable "enable_monitoring" {
type = bool
default = true
}
variable "is_production" {
type = bool
default = false
}
locals {
monitoring_enabled = true
backup_disabled = false
# Shartli ifodalar
needs_backup = var.environment == "prod" ? true : false
}
Collection Types (To'plamlar)
1. list (Ro'yxat)
# String list
variable "availability_zones" {
type = list(string)
default = ["us-east-1a", "us-east-1b", "us-east-1c"]
}
# Number list
variable "allowed_ports" {
type = list(number)
default = [80, 443, 8080]
}
locals {
# Oddiy list
zones = ["us-east-1a", "us-east-1b"]
# List ichida list
nested = [
["a", "b"],
["c", "d"]
]
# List elementlariga kirish
first_zone = var.availability_zones[0]
second_zone = var.availability_zones[1]
# List uzunligi
zone_count = length(var.availability_zones)
}
2. set (To'plam)
variable "security_group_ids" {
type = set(string)
default = ["sg-123456", "sg-789012"]
}
locals {
# Set yaratish
unique_ids = toset(["id1", "id2", "id1"]) # ["id1", "id2"]
# Set operatsiyalari
set1 = toset(["a", "b", "c"])
set2 = toset(["b", "c", "d"])
# Union
combined = setunion(set1, set2) # ["a", "b", "c", "d"]
# Intersection
common = setintersection(set1, set2) # ["b", "c"]
}
3. map (Xarita)
variable "tags" {
type = map(string)
default = {
Environment = "dev"
Project = "MyApp"
Owner = "DevOps Team"
}
}
variable "instance_types" {
type = map(string)
default = {
dev = "t2.micro"
staging = "t2.small"
prod = "t3.large"
}
}
locals {
# Map yaratish
config = {
region = "us-east-1"
zone = "us-east-1a"
}
# Map ichida map
environments = {
dev = {
instance_type = "t2.micro"
count = 1
}
prod = {
instance_type = "t3.large"
count = 5
}
}
# Map'dan qiymat olish
dev_type = local.environments["dev"]["instance_type"]
# Lookup function bilan
type = lookup(var.instance_types, var.environment, "t2.micro")
}
Structural Types (Strukturaviy turlar)
1. object (Obyekt)
variable "instance_config" {
type = object({
instance_type = string
ami_id = string
disk_size = number
monitoring = bool
tags = map(string)
})
default = {
instance_type = "t2.micro"
ami_id = "ami-123456"
disk_size = 20
monitoring = true
tags = {
Name = "web"
Env = "dev"
}
}
}
# Object ichida object
variable "vpc_config" {
type = object({
cidr_block = string
subnets = list(object({
cidr_block = string
availability_zone = string
}))
})
default = {
cidr_block = "10.0.0.0/16"
subnets = [
{
cidr_block = "10.0.1.0/24"
availability_zone = "us-east-1a"
},
{
cidr_block = "10.0.2.0/24"
availability_zone = "us-east-1b"
}
]
}
}
2. tuple (Kortej)
# Turli xil turlar bitta ro'yxatda
variable "mixed_config" {
type = tuple([string, number, bool])
default = ["web-server", 8080, true]
}
locals {
# Tuple yaratish
config = tuple(["name", 123, true, ["nested"]])
# Tuple elementlariga kirish
name = local.config[0] # "name"
number = local.config[1] # 123
enabled = local.config[2] # true
}
Complex Types (Murakkab turlar)
# List of objects
variable "instances" {
type = list(object({
name = string
instance_type = string
disk_size = number
tags = map(string)
}))
default = [
{
name = "web-1"
instance_type = "t2.micro"
disk_size = 20
tags = {
Role = "web"
}
},
{
name = "web-2"
instance_type = "t2.small"
disk_size = 30
tags = {
Role = "api"
}
}
]
}
# Map of objects
variable "environments" {
type = map(object({
instance_type = string
instance_count = number
disk_size = number
backup_enabled = bool
}))
default = {
dev = {
instance_type = "t2.micro"
instance_count = 1
disk_size = 20
backup_enabled = false
}
prod = {
instance_type = "t3.large"
instance_count = 5
disk_size = 100
backup_enabled = true
}
}
}
Type Conversion (Turni o'zgartirish)
locals {
# String ga o'tkazish
num_to_string = tostring(123) # "123"
bool_to_string = tostring(true) # "true"
# Number ga o'tkazish
string_to_num = tonumber("456") # 456
# Bool ga o'tkazish
string_to_bool = tobool("true") # true
num_to_bool = tobool(1) # true
# List ga o'tkazish
set_to_list = tolist(toset(["a", "b", "a"])) # ["a", "b"]
# Set ga o'tkazish
list_to_set = toset(["a", "b", "a"]) # ["a", "b"]
# Map ga o'tkazish
obj_to_map = tomap({
key1 = "value1"
key2 = "value2"
})
}
Ifodalar (Expressions)
Arifmetik operatorlar
locals {
# Asosiy operatorlar
sum = 5 + 3 # 8
difference = 10 - 4 # 6
product = 3 * 4 # 12
quotient = 20 / 4 # 5
modulo = 17 % 5 # 2
# Murakkab hisoblashlar
memory_mb = var.instance_count * 1024
disk_gb = var.disk_size_gb * (var.replica_count + 1)
cpu_total = var.instance_count * var.cpu_per_instance
# Operator prioriteti
result = 2 + 3 * 4 # 14 (not 20)
result_with_parens = (2 + 3) * 4 # 20
# Negative raqamlar
negative = -5
absolute = abs(negative) # 5
}
Taqqoslash operatorlari
locals {
# Teng
is_equal = 5 == 5 # true
string_equal = "hello" == "hello" # true
# Teng emas
not_equal = 5 != 3 # true
# Kattaroq
greater = 10 > 5 # true
# Kichikroq
less = 3 < 7 # true
# Kattaroq yoki teng
greater_equal = 5 >= 5 # true
# Kichikroq yoki teng
less_equal = 4 <= 6 # true
# String taqqoslash
str_compare = "abc" < "xyz" # true (lexicographic)
}
Mantiqiy operatorlar
locals {
# AND (va)
and_result = true && true # true
and_false = true && false # false
# OR (yoki)
or_result = true || false # true
or_false = false || false # false
# NOT (inkor)
not_true = !true # false
not_false = !false # true
# Murakkab ifodalar
is_prod_us = (
var.environment == "prod" &&
var.region == "us-east-1"
)
needs_backup = (
var.environment == "prod" ||
var.environment == "staging"
)
is_valid = (
var.instance_count > 0 &&
var.instance_count <= 10 &&
var.disk_size >= 20
)
# Short-circuit evaluation
safe_check = var.config != null && var.config.enabled
}
Shartli ifodalar (Ternary operator)
locals {
# Sintaksis: condition ? true_value : false_value
# Oddiy shart
instance_type = var.environment == "prod" ? "t3.large" : "t2.micro"
# Number uchun
disk_size = var.environment == "prod" ? 100 : 20
# Bool uchun
monitoring = var.environment == "prod" ? true : false
# String birlashtirish
bucket_name = var.environment == "prod" ?
"${var.project}-prod-bucket" :
"${var.project}-dev-bucket"
# Nested ternary (ko'p shartlar)
instance_count = (
var.environment == "prod" ? 5 :
var.environment == "staging" ? 3 :
1 # default
)
# Null coalescence
final_value = var.custom_value != null ? var.custom_value : "default"
}
# Resource ichida ishlatish
resource "aws_instance" "web" {
instance_type = var.environment == "prod" ? "t3.large" : "t2.micro"
monitoring = var.environment == "prod" ? true : false
tags = {
Name = var.environment == "prod" ? "prod-server" : "dev-server"
Size = var.environment == "prod" ? "large" : "small"
}
root_block_device {
volume_size = var.environment == "prod" ? 100 : 20
}
}
String operatsiyalari
String interpolatsiya
locals {
# Oddiy interpolatsiya
greeting = "Hello, ${var.username}!"
# O'zgaruvchilarni birlashtirish
full_name = "${var.first_name} ${var.last_name}"
# Hisoblashlar bilan
message = "You have ${var.item_count * 2} items"
# Shartli ifodalar bilan
status = "Server is ${var.enabled ? "running" : "stopped"}"
# Resource attribute'dan
instance_info = "Instance ${aws_instance.web.id} has IP ${aws_instance.web.public_ip}"
# Murakkab ifodalar
report = "Total cost: $${var.price_per_unit * var.quantity}"
# Escape qilish
escaped = "The value is: $${var.value}" # Literal ${}
}
String funksiyalari
locals {
original = " Hello World "
# upper - katta harfga
uppercase = upper("hello") # "HELLO"
# lower - kichik harfga
lowercase = lower("WORLD") # "world"
# title - har bir so'zni katta harf bilan
title_case = title("hello world") # "Hello World"
# trim - chetdagi bo'shliqlarni olib tashlash
trimmed = trim(local.original) # "Hello World"
trimspace = trimspace(local.original) # "Hello World"
# trimprefix - boshidan olib tashlash
no_prefix = trimprefix("aws_instance", "aws_") # "instance"
# trimsuffix - oxiridan olib tashlash
no_suffix = trimsuffix("file.txt", ".txt") # "file"
# split - ajratish
parts = split(",", "a,b,c,d") # ["a", "b", "c", "d"]
words = split(" ", "hello world") # ["hello", "world"]
# join - birlashtirish
joined = join(", ", ["a", "b", "c"]) # "a, b, c"
path = join("/", ["usr", "local", "bin"]) # "usr/local/bin"
# replace - almashtirish
replaced = replace("hello world", "world", "terraform") # "hello terraform"
# substr - qismini olish
substring = substr("hello world", 0, 5) # "hello"
last_chars = substr("hello", -2, -1) # "lo"
# format - formatlab chiqarish
formatted = format("Server-%03d", 42) # "Server-042"
complex_format = format("The %s has %d items", "basket", 5)
# contains - mavjudligini tekshirish
has_world = strcontains("hello world", "world") # true
# regex - regular expression
matches = regex("^[a-z]+$", "hello") # true
extracted = regex("\\d+", "server-123") # "123"
}
Heredoc (Ko'p qatorli string)
locals {
# Oddiy heredoc
script = <<EOF
#!/bin/bash
echo "Hello World"
apt-get update
apt-get install -y nginx
EOF
# Indented heredoc (<<-)
indented_script = <<-EOT
#!/bin/bash
echo "This is indented"
systemctl start nginx
systemctl enable nginx
EOT
# JSON heredoc
json_config = <<-JSON
{
"name": "web-server",
"port": 80,
"enabled": true,
"tags": ["web", "production"]
}
JSON
# YAML heredoc
yaml_config = <<-YAML
name: web-server
port: 80
enabled: true
tags:
- web
- production
YAML
# Interpolatsiya bilan
user_data = <<-EOF
#!/bin/bash
echo "Environment: ${var.environment}"
echo "Region: ${var.region}"
apt-get update
EOF
}
# Resource ichida heredoc
resource "aws_instance" "web" {
ami = "ami-123456"
instance_type = "t2.micro"
user_data = <<-EOF
#!/bin/bash
apt-get update
apt-get install -y nginx
cat > /var/www/html/index.html <<HTML
<html>
<body>
<h1>Hello from ${var.environment}</h1>
<p>Instance: ${aws_instance.web.id}</p>
</body>
</html>
HTML
systemctl start nginx
systemctl enable nginx
EOF
}