Preconditions & Postconditions
Custom Conditions (Terraform 1.2+) — assertion ที่ตรวจสอบ assumption ใน config — ป้องกัน bug ที่ output แต่ค่าผิด
Custom Conditions มีอะไรบ้าง?
3 แบบหลัก:
| Block | อยู่ใน | ตรวจตอน |
|---|---|---|
validation | variable | input validation |
precondition | resource, data, output | ก่อน apply |
postcondition | resource, data, output | หลัง apply |
Output Precondition
output "instance_public_ip" {
value = aws_instance.web.public_ip
precondition {
condition = aws_instance.web.public_ip != ""
error_message = "Instance has no public IP. Did you set associate_public_ip_address?"
}
}
ถ้า condition fail → Terraform error ก่อน return output
ตัวอย่าง: Ensure Output ไม่ Empty
output "alb_dns_name" {
value = aws_alb.main.dns_name
precondition {
condition = aws_alb.main.dns_name != ""
error_message = "ALB DNS name is empty. Check ALB creation status."
}
}
Resource Preconditions
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = "t2.micro"
lifecycle {
precondition {
condition = data.aws_ami.ubuntu.architecture == "x86_64"
error_message = "AMI must be x86_64 architecture for t2.micro."
}
precondition {
condition = var.environment != "prod" || var.instance_type != "t2.micro"
error_message = "Production must use t3.large or above."
}
}
}
Data Source Postconditions
data "aws_ami" "latest_ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
lifecycle {
postcondition {
condition = self.architecture == "x86_64"
error_message = "Found AMI is not x86_64."
}
postcondition {
condition = timecmp(self.creation_date, "2024-01-01") > 0
error_message = "AMI is too old (created before 2024-01-01)."
}
}
}
เปรียบเทียบ: validation vs precondition vs postcondition
validation (variable block)
ตรวจ input ก่อนเริ่ม plan
variable "instance_type" {
validation {
condition = startswith(var.instance_type, "t3")
error_message = "Only t3 family allowed."
}
}
precondition (resource/output)
ตรวจ before resource is read/created — ใช้ assumption check
resource "aws_instance" "web" {
lifecycle {
precondition {
condition = data.aws_subnet.this.availability_zone == "ap-southeast-1a"
error_message = "Must deploy in ap-southeast-1a."
}
}
}
postcondition (resource/data)
ตรวจ after resource is created/read — ใช้ verify result
data "aws_db_instance" "prod" {
db_instance_identifier = "prod-db"
lifecycle {
postcondition {
condition = self.engine == "postgres"
error_message = "Expected postgres engine."
}
}
}
ตัวอย่าง: Real-World Patterns
Pattern 1: Validate AMI Architecture
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"]
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-*"]
}
lifecycle {
postcondition {
condition = self.architecture == var.expected_arch
error_message = "AMI architecture mismatch. Expected ${var.expected_arch}, got ${self.architecture}."
}
}
}
Pattern 2: Ensure VPC Has Sufficient Subnets
data "aws_vpc" "selected" {
id = var.vpc_id
}
data "aws_subnets" "private" {
filter {
name = "vpc-id"
values = [data.aws_vpc.selected.id]
}
filter {
name = "tag:Tier"
values = ["Private"]
}
lifecycle {
postcondition {
condition = length(self.ids) >= 2
error_message = "VPC must have at least 2 private subnets across AZs."
}
}
}
Pattern 3: Validate Pre-Deploy Conditions
resource "aws_lambda_function" "api" {
function_name = "api"
role = aws_iam_role.lambda.arn
handler = "index.handler"
runtime = "nodejs20.x"
s3_bucket = aws_s3_bucket.code.id
s3_key = "lambda/api.zip"
lifecycle {
precondition {
condition = aws_iam_role_policy_attachment.lambda_basic.role == aws_iam_role.lambda.name
error_message = "Lambda role must have basic execution policy attached."
}
precondition {
condition = data.aws_s3_object.lambda_code.content_length > 0
error_message = "Lambda code zip is empty."
}
}
}
Pattern 4: Output Verification
output "api_endpoint" {
value = "https://${aws_apigatewayv2_api.main.api_endpoint}"
precondition {
condition = aws_apigatewayv2_stage.main.deployment_id != ""
error_message = "API Gateway stage not deployed yet."
}
}
self ใน Postcondition
ใน postcondition ใช้ self.<attr> อ้างอิง resource ตัวเอง (จะใช้ aws_instance.web.attr ไม่ได้ เพราะวงกลม):
resource "aws_instance" "web" {
ami = "ami-12345"
instance_type = "t2.micro"
lifecycle {
postcondition {
condition = self.public_ip != "" # ← self
error_message = "Instance has no public IP."
}
}
}
สรุป
| Type | Where | When | Use Case |
|---|---|---|---|
validation | variable | input | check input values |
precondition | resource/data/output | ก่อน read/create | check assumption |
postcondition | resource/data | หลัง read/create | verify result |
Best Practices:
- ใช้ validation บน variable input
- ใช้ precondition ใน output → ป้องกัน return ค่าว่าง/ผิด
- ใช้ postcondition ใน data source → verify ดึงข้อมูลถูก
- Error message ชัดเจน + actionable
ต่อไป → Section 7: Format & Validate