By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.
(For Cloud Engineers, DevOps, and Certification Takers)
You’re deploying a VPC with a private subnet, an RDS database, and an EC2 instance that needs to connect to that database. You write the Terraform config, run terraform apply, and… the EC2 instance launches before the RDS endpoint is ready. The app crashes, logs fill with Connection refused, and your pager goes off at 2 AM.
terraform apply
Connection refused
This is why resource attributes and dependencies matter.
id
arn
tags
depends_on
In production, ignoring these means:❌ Race conditions (resources created in the wrong order).❌ Broken references (e.g., trying to use an arn that doesn’t exist yet).❌ Tagging chaos (cost tracking, security compliance, and automation all rely on tags).❌ Failed audits (missing Name tags? That’s a red flag in most enterprise environments).
Name
Real-world scenario:You inherit a Terraform repo where someone hardcoded an S3 bucket name (my-bucket-123) instead of using the arn attribute. Later, the bucket name changes, and every reference breaks. Now you’re debugging a 500-line config instead of just updating one line.
my-bucket-123
aws_instance.web.id
aws_s3_bucket.data.arn
terraform destroy
apply
i-0abc1234567890def
arn:aws:s3:::my-bucket
Environment = "prod"
Owner = "team-infra"
Condition: { "StringEquals": { "aws:ResourceTag/Environment": "prod" } }
vpc_id = aws_vpc.main.id
output "db_endpoint" { value = aws_db_instance.main.endpoint }
data "aws_vpc" "default" { default = true }
terraform --version
aws sts get-caller-identity
Deploy: 1. A VPC with a private subnet.2. An RDS PostgreSQL database (private).3. An EC2 instance (in the same VPC) that connects to the RDS.4. Tags for cost tracking and security.5. Explicit dependencies to ensure the RDS is ready before the EC2 launches.
mkdir tf-attributes-dependencies && cd tf-attributes-dependencies touch main.tf
main.tf
terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } } provider "aws" { region = "us-east-1" } # 1. VPC + Subnet resource "aws_vpc" "main" { cidr_block = "10.0.0.0/16" tags = { Name = "main-vpc" Environment = "dev" Owner = "team-infra" } } resource "aws_subnet" "private" { vpc_id = aws_vpc.main.id # Implicit dependency cidr_block = "10.0.1.0/24" availability_zone = "us-east-1a" tags = { Name = "private-subnet" } } # 2. Security Group for RDS resource "aws_security_group" "rds" { name = "rds-sg" description = "Allow PostgreSQL traffic from EC2" vpc_id = aws_vpc.main.id ingress { from_port = 5432 to_port = 5432 protocol = "tcp" cidr_blocks = [aws_subnet.private.cidr_block] # Reference subnet CIDR } tags = { Name = "rds-sg" } } # 3. RDS PostgreSQL (private) resource "aws_db_instance" "postgres" { identifier = "my-postgres-db" engine = "postgres" engine_version = "15.4" instance_class = "db.t3.micro" allocated_storage = 20 db_name = "mydb" username = "admin" password = "SuperSecret123!" # ⚠️ Use AWS Secrets Manager in prod! db_subnet_group_name = aws_db_subnet_group.main.name vpc_security_group_ids = [aws_security_group.rds.id] skip_final_snapshot = true # ⚠️ Disable in prod! tags = { Name = "postgres-db" Environment = "dev" } } resource "aws_db_subnet_group" "main" { name = "main-subnet-group" subnet_ids = [aws_subnet.private.id] tags = { Name = "main-subnet-group" } } # 4. IAM Role for EC2 (to access RDS) resource "aws_iam_role" "ec2_rds_access" { name = "ec2-rds-access-role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "ec2.amazonaws.com" } } ] }) } resource "aws_iam_role_policy_attachment" "rds_full_access" { role = aws_iam_role.ec2_rds_access.name policy_arn = "arn:aws:iam::aws:policy/AmazonRDSFullAccess" } # 5. EC2 Instance (depends on RDS + IAM role) resource "aws_instance" "app" { ami = "ami-0c55b159cbfafe1f0" # Amazon Linux 2 (us-east-1) instance_type = "t3.micro" subnet_id = aws_subnet.private.id iam_instance_profile = aws_iam_instance_profile.ec2_rds_access.name # Explicit dependency: Wait for RDS to be ready depends_on = [aws_db_instance.postgres, aws_iam_role.ec2_rds_access] user_data = <<-EOF #!/bin/bash yum install -y postgresql echo "RDS endpoint: ${aws_db_instance.postgres.endpoint}" >> /home/ec2-user/rds_endpoint.txt EOF tags = { Name = "app-server" Environment = "dev" Role = "database-client" } } resource "aws_iam_instance_profile" "ec2_rds_access" { name = "ec2-rds-access-profile" role = aws_iam_role.ec2_rds_access.name } # 6. Output the RDS endpoint for debugging output "rds_endpoint" { value = aws_db_instance.postgres.endpoint }
terraform init terraform plan # Verify no errors terraform apply -auto-approve
Check the RDS endpoint: bash terraform output rds_endpoint Output should look like: my-postgres-db.abc123xyz.us-east-1.rds.amazonaws.com:5432.
bash terraform output rds_endpoint
my-postgres-db.abc123xyz.us-east-1.rds.amazonaws.com:5432
SSH into the EC2 instance and check the RDS endpoint: bash ssh -i your-key.pem ec2-user@<EC2_PUBLIC_IP> cat /home/ec2-user/rds_endpoint.txt (Replace <EC2_PUBLIC_IP> with the instance’s public IP from the AWS Console.)
bash ssh -i your-key.pem ec2-user@<EC2_PUBLIC_IP> cat /home/ec2-user/rds_endpoint.txt
<EC2_PUBLIC_IP>
Check tags in AWS Console:
terraform destroy -auto-approve
hcl password = var.db_password # Pass via Terraform variables or AWS Secrets Manager
AmazonRDSFullAccess
hcl policy_arn = "arn:aws:iam::aws:policy/AmazonRDSReadOnlyAccess"
Environment=prod
hcl tags = { CostCenter = "marketing" Project = "website-redesign" }
lifecycle
hcl resource "aws_db_instance" "postgres" { lifecycle { prevent_destroy = true # ⚠️ Terraform will error if you try to destroy this } }
outputs
# In main.tf module "vpc" { source = "./vpc_module" }
resource "aws_subnet" "private" { vpc_id = module.vpc.vpc_id # Reference the output } `` - Standardize tagging: Enforce a tagging schema (e.g.,Environment,Owner,Project`).
`` - Standardize tagging: Enforce a tagging schema (e.g.,
,
DatabaseConnections
/var/log/cloud-init-output.log
destroy
vpc_id
rds_endpoint
prevent_destroy = true
✅ aws_s3_bucket.data.arn (dynamic)
"When should you use depends_on?"
❌ When you can use an implicit dependency (e.g., vpc_id = aws_vpc.main.id).
"How do you ensure an EC2 instance is created after an RDS database?"
depends_on = [aws_db_instance.postgres]
"You need to deploy a Lambda function that reads from an S3 bucket. The bucket must exist before the Lambda is created. How do you ensure this?"
✅ Answer:
resource "aws_lambda_function" "example" { # ... other config ... depends_on = [aws_s3_bucket.data] # Explicit dependency }
Why?- The Lambda needs the bucket to exist, but there’s no attribute reference in the Lambda config.- depends_on forces Terraform to create the bucket first.
Challenge:Deploy an S3 bucket and an IAM policy that grants read access to the bucket. The IAM policy must use the bucket’s ARN dynamically (no hardcoding). After deployment, verify the policy works by uploading a file to the bucket and checking if an IAM user can read it.
Solution:
resource "aws_s3_bucket" "data" { bucket = "my-unique-bucket-name-123" # Must be globally unique! } resource "aws_iam_policy" "bucket_read" { name = "s3-read-policy" description = "Allows read access to the S3 bucket" policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = ["s3:GetObject"] Effect = "Allow" Resource = aws_s3_bucket.data.arn # Dynamic ARN reference } ] }) } # Verify by attaching the policy to a user and testing access
Why it works:- The IAM policy uses aws_s3_bucket.data.arn to dynamically reference the bucket ARN.- If the bucket name changes, the policy automatically updates.
output "vpc_id" { value = aws_vpc.main.id }
tags = { Name = "my-resource", Environment = "prod" }
Environment
skip_final_snapshot = false
final_snapshot_identifier
my-bucket-123xyz
<org>-<project>-<env>
arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
Join 4M+ learners. Unlock unlimited quizzes, wrong-answer tracking, flashcards + reminders, study guides, and 1-on-1 challenges.