+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Part 523 of 541

๐Ÿ“˜ Terraform: Infrastructure as Code

Master terraform: infrastructure as code in Python with practical examples, best practices, and real-world applications ๐Ÿš€

๐Ÿ’ŽAdvanced
25 min read

Prerequisites

  • Basic understanding of programming concepts ๐Ÿ“
  • Python installation (3.8+) ๐Ÿ
  • VS Code or preferred IDE ๐Ÿ’ป

What you'll learn

  • Understand the concept fundamentals ๐ŸŽฏ
  • Apply the concept in real projects ๐Ÿ—๏ธ
  • Debug common issues ๐Ÿ›
  • Write clean, Pythonic code โœจ

๐ŸŽฏ Introduction

Welcome to this exciting tutorial on Terraform and Infrastructure as Code! ๐ŸŽ‰ In this guide, weโ€™ll explore how to manage your infrastructure using code, making deployments repeatable, version-controlled, and automated.

Youโ€™ll discover how Terraform can transform your infrastructure management from manual, error-prone processes to automated, reliable workflows. Whether youโ€™re managing cloud resources ๐ŸŒ, containers ๐Ÿณ, or hybrid infrastructure ๐Ÿ—๏ธ, understanding Infrastructure as Code is essential for modern DevOps practices.

By the end of this tutorial, youโ€™ll feel confident using Terraform with Python to automate your infrastructure! Letโ€™s dive in! ๐ŸŠโ€โ™‚๏ธ

๐Ÿ“š Understanding Infrastructure as Code

๐Ÿค” What is Infrastructure as Code?

Infrastructure as Code (IaC) is like writing a recipe for your favorite dish ๐Ÿณ. Instead of manually setting up servers and configuring resources each time, you write code that describes exactly what you want, and tools like Terraform create it for you automatically!

In DevOps terms, IaC means managing and provisioning infrastructure through machine-readable definition files, rather than physical hardware configuration or interactive configuration tools. This means you can:

  • โœจ Version control your infrastructure
  • ๐Ÿš€ Automate deployments and scaling
  • ๐Ÿ›ก๏ธ Ensure consistency across environments

๐Ÿ’ก Why Use Terraform?

Hereโ€™s why developers love Terraform:

  1. Declarative Syntax ๐Ÿ”’: Describe what you want, not how to build it
  2. Cloud Agnostic ๐Ÿ’ป: Works with AWS, Azure, GCP, and more
  3. State Management ๐Ÿ“–: Tracks whatโ€™s deployed and manages changes
  4. Modular Design ๐Ÿ”ง: Reuse infrastructure patterns

Real-world example: Imagine deploying a web application ๐ŸŒ. With Terraform, you can define your entire stack (servers, databases, load balancers) in code and deploy it with a single command!

๐Ÿ”ง Basic Syntax and Usage

๐Ÿ“ Getting Started with Terraform and Python

Letโ€™s start with installing and using Terraform with Python:

# ๐Ÿ‘‹ Hello, Terraform from Python!
import subprocess
import json
import os

# ๐ŸŽจ Python wrapper for Terraform commands
class TerraformWrapper:
    def __init__(self, working_dir="."):
        self.working_dir = working_dir
        
    def init(self):
        """๐Ÿš€ Initialize Terraform in the working directory"""
        result = subprocess.run(
            ["terraform", "init"],
            cwd=self.working_dir,
            capture_output=True,
            text=True
        )
        return result.returncode == 0
    
    def plan(self):
        """๐Ÿ“‹ Create an execution plan"""
        result = subprocess.run(
            ["terraform", "plan", "-out=tfplan"],
            cwd=self.working_dir,
            capture_output=True,
            text=True
        )
        return result.stdout
    
    def apply(self):
        """๐Ÿš€ Apply the changes"""
        result = subprocess.run(
            ["terraform", "apply", "-auto-approve", "tfplan"],
            cwd=self.working_dir,
            capture_output=True,
            text=True
        )
        return result.returncode == 0

๐Ÿ’ก Explanation: This Python wrapper makes it easy to control Terraform from your Python applications!

๐ŸŽฏ Writing Your First Terraform Configuration

Hereโ€™s a simple Terraform configuration file (main.tf):

# ๐Ÿ—๏ธ Define the provider (AWS in this example)
provider "aws" {
  region = "us-west-2"  # ๐ŸŒŽ Choose your region
}

# ๐Ÿ–ฅ๏ธ Create an EC2 instance
resource "aws_instance" "web_server" {
  ami           = "ami-0c55b159cbfafe1f0"  # ๐Ÿ“ฆ Amazon Linux 2
  instance_type = "t2.micro"                # ๐Ÿ’ฐ Free tier eligible!
  
  tags = {
    Name        = "PythonTerraformDemo"   # ๐Ÿท๏ธ Name your instance
    Environment = "Development"            # ๐Ÿš€ Track environments
    ManagedBy   = "Terraform"             # ๐Ÿค– Automation FTW!
  }
}

# ๐Ÿ“ค Output the public IP
output "public_ip" {
  value = aws_instance.web_server.public_ip
  description = "๐ŸŒ The public IP of our web server"
}

๐Ÿ’ก Practical Examples

๐Ÿ›’ Example 1: E-commerce Infrastructure

Letโ€™s build infrastructure for an online store:

# ๐Ÿ›๏ธ Generate Terraform configuration from Python
class EcommerceInfrastructure:
    def __init__(self, project_name):
        self.project_name = project_name
        self.resources = []
    
    def add_web_servers(self, count=2):
        """๐ŸŒ Add load-balanced web servers"""
        for i in range(count):
            server = {
                "type": "aws_instance",
                "name": f"web_server_{i}",
                "config": {
                    "ami": "ami-0c55b159cbfafe1f0",
                    "instance_type": "t3.small",
                    "tags": {
                        "Name": f"{self.project_name}-web-{i}",
                        "Role": "WebServer",
                        "Emoji": "๐ŸŒ"
                    }
                }
            }
            self.resources.append(server)
    
    def add_database(self):
        """๐Ÿ—„๏ธ Add RDS database"""
        db = {
            "type": "aws_db_instance",
            "name": "main_database",
            "config": {
                "engine": "postgres",
                "instance_class": "db.t3.micro",
                "allocated_storage": 20,
                "db_name": self.project_name,
                "tags": {
                    "Name": f"{self.project_name}-db",
                    "Role": "Database",
                    "Emoji": "๐Ÿ—„๏ธ"
                }
            }
        }
        self.resources.append(db)
    
    def generate_terraform(self):
        """๐Ÿ“ Generate Terraform configuration"""
        tf_config = []
        
        # ๐ŸŽฏ Add provider
        tf_config.append('''
provider "aws" {
  region = "us-west-2"
}
''')
        
        # ๐Ÿ—๏ธ Add resources
        for resource in self.resources:
            resource_block = f'''
resource "{resource['type']}" "{resource['name']}" {{
'''
            for key, value in resource['config'].items():
                if isinstance(value, dict):
                    resource_block += f"  {key} = {{\n"
                    for k, v in value.items():
                        resource_block += f'    {k} = "{v}"\n'
                    resource_block += "  }\n"
                elif isinstance(value, str):
                    resource_block += f'  {key} = "{value}"\n'
                else:
                    resource_block += f'  {key} = {value}\n'
            resource_block += "}\n"
            tf_config.append(resource_block)
        
        return ''.join(tf_config)

# ๐ŸŽฎ Let's use it!
infra = EcommerceInfrastructure("MyShop")
infra.add_web_servers(2)
infra.add_database()

# ๐Ÿ’พ Save to file
with open("main.tf", "w") as f:
    f.write(infra.generate_terraform())

print("โœ… Terraform configuration generated!")

๐ŸŽฏ Try it yourself: Add a load balancer and auto-scaling group!

๐ŸŽฎ Example 2: Game Server Infrastructure

Letโ€™s create infrastructure for a multiplayer game:

# ๐Ÿ† Dynamic game server infrastructure
import yaml
import json

class GameInfrastructure:
    def __init__(self):
        self.regions = []
        self.servers = {}
        
    def add_region(self, region, player_capacity):
        """๐ŸŒ Add a new game region"""
        self.regions.append({
            "name": region,
            "capacity": player_capacity,
            "servers": []
        })
        print(f"๐ŸŒ Added region: {region} (capacity: {player_capacity} players)")
    
    def calculate_servers_needed(self, region, current_players):
        """๐Ÿงฎ Calculate required servers based on load"""
        servers_per_100_players = 1
        servers_needed = max(1, (current_players // 100) + 1)
        
        return {
            "region": region,
            "servers_needed": servers_needed,
            "type": "t3.medium" if current_players > 500 else "t3.small"
        }
    
    def generate_terraform_vars(self, player_data):
        """๐Ÿ“Š Generate Terraform variables based on player data"""
        terraform_vars = {
            "game_servers": {},
            "load_balancers": {},
            "databases": {}
        }
        
        for region, players in player_data.items():
            server_info = self.calculate_servers_needed(region, players)
            
            # ๐Ÿ–ฅ๏ธ Define servers
            terraform_vars["game_servers"][region] = {
                "count": server_info["servers_needed"],
                "instance_type": server_info["type"],
                "tags": {
                    "Region": region,
                    "Role": "GameServer",
                    "MaxPlayers": 100,
                    "Emoji": "๐ŸŽฎ"
                }
            }
            
            # โš–๏ธ Add load balancer for each region
            terraform_vars["load_balancers"][region] = {
                "name": f"game-lb-{region}",
                "health_check_path": "/health",
                "emoji": "โš–๏ธ"
            }
        
        return terraform_vars
    
    def create_monitoring_dashboard(self):
        """๐Ÿ“Š Create CloudWatch dashboard config"""
        dashboard_config = {
            "widgets": [
                {
                    "type": "metric",
                    "properties": {
                        "metrics": [
                            ["AWS/EC2", "CPUUtilization", {"stat": "Average"}],
                            ["AWS/EC2", "NetworkIn", {"stat": "Sum"}],
                            ["Custom", "ActivePlayers", {"stat": "Sum"}]
                        ],
                        "period": 300,
                        "stat": "Average",
                        "region": "us-west-2",
                        "title": "๐ŸŽฎ Game Server Metrics"
                    }
                }
            ]
        }
        return json.dumps(dashboard_config, indent=2)

# ๐ŸŽฎ Usage example
game_infra = GameInfrastructure()
game_infra.add_region("us-west-2", 1000)
game_infra.add_region("eu-west-1", 1500)

# ๐Ÿ“Š Current player counts
player_data = {
    "us-west-2": 750,
    "eu-west-1": 1200
}

# ๐Ÿ—๏ธ Generate infrastructure config
tf_vars = game_infra.generate_terraform_vars(player_data)

# ๐Ÿ’พ Save as terraform.tfvars.json
with open("terraform.tfvars.json", "w") as f:
    json.dump(tf_vars, f, indent=2)

print("โœ… Game infrastructure configuration ready!")

๐Ÿš€ Advanced Concepts

๐Ÿง™โ€โ™‚๏ธ Dynamic Infrastructure with Python

When youโ€™re ready to level up, try this advanced pattern:

# ๐ŸŽฏ Advanced: Dynamic infrastructure based on metrics
from datetime import datetime
import boto3

class AutoScalingInfrastructure:
    def __init__(self):
        self.cloudwatch = boto3.client('cloudwatch')
        self.terraform_state = {}
    
    def analyze_metrics(self, metric_name, threshold):
        """๐Ÿ“Š Analyze CloudWatch metrics"""
        response = self.cloudwatch.get_metric_statistics(
            Namespace='AWS/EC2',
            MetricName=metric_name,
            StartTime=datetime.now().replace(hour=datetime.now().hour-1),
            EndTime=datetime.now(),
            Period=300,
            Statistics=['Average']
        )
        
        # ๐ŸŽฏ Check if scaling is needed
        if response['Datapoints']:
            avg_value = sum(d['Average'] for d in response['Datapoints']) / len(response['Datapoints'])
            return avg_value > threshold
        return False
    
    def generate_scaling_config(self, scale_up=False):
        """๐Ÿš€ Generate Terraform config for scaling"""
        base_count = self.terraform_state.get('instance_count', 2)
        new_count = base_count + 2 if scale_up else max(2, base_count - 1)
        
        config = f'''
variable "instance_count" {{
  default = {new_count}
}}

resource "aws_autoscaling_group" "app_asg" {{
  min_size         = 2
  max_size         = 10
  desired_capacity = var.instance_count
  
  tag {{
    key                 = "Name"
    value               = "AutoScaled-Instance"
    propagate_at_launch = true
  }}
  
  tag {{
    key                 = "ScaledAt"
    value               = "{datetime.now().isoformat()}"
    propagate_at_launch = true
  }}
  
  tag {{
    key                 = "Emoji"
    value               = "{"๐Ÿš€" if scale_up else "๐Ÿ“‰"}"
    propagate_at_launch = true
  }}
}}
'''
        return config

๐Ÿ—๏ธ Infrastructure Testing with Python

For the brave developers:

# ๐Ÿงช Test your infrastructure before deploying
import unittest
import hcl2
import lark

class TerraformValidator:
    def __init__(self, tf_file_path):
        self.tf_file_path = tf_file_path
        self.config = None
    
    def parse_terraform(self):
        """๐Ÿ“ Parse Terraform configuration"""
        try:
            with open(self.tf_file_path, 'r') as f:
                self.config = hcl2.load(f)
            return True
        except lark.exceptions.UnexpectedInput as e:
            print(f"โŒ Syntax error in Terraform file: {e}")
            return False
    
    def validate_resources(self):
        """โœ… Validate resource configurations"""
        errors = []
        
        if 'resource' in self.config:
            for resource_type, resources in self.config['resource'].items():
                for name, config in resources.items():
                    # ๐Ÿ” Check required fields
                    if resource_type == 'aws_instance':
                        if 'ami' not in config:
                            errors.append(f"โŒ Missing AMI in {name}")
                        if 'instance_type' not in config:
                            errors.append(f"โŒ Missing instance_type in {name}")
                    
                    # ๐Ÿท๏ธ Check tags
                    if 'tags' not in config:
                        errors.append(f"โš ๏ธ No tags defined for {name}")
        
        return errors

# ๐Ÿงช Unit tests for infrastructure
class TestInfrastructure(unittest.TestCase):
    def setUp(self):
        self.validator = TerraformValidator("main.tf")
    
    def test_terraform_syntax(self):
        """๐Ÿ” Test Terraform syntax is valid"""
        self.assertTrue(self.validator.parse_terraform())
    
    def test_resource_validation(self):
        """โœ… Test all resources are properly configured"""
        self.validator.parse_terraform()
        errors = self.validator.validate_resources()
        self.assertEqual(len(errors), 0, f"Validation errors: {errors}")

โš ๏ธ Common Pitfalls and Solutions

๐Ÿ˜ฑ Pitfall 1: State File Conflicts

# โŒ Wrong way - Multiple people modifying state
# This leads to conflicts and lost changes!
terraform_wrapper.apply()  # ๐Ÿ’ฅ Conflicts if someone else is applying!

# โœ… Correct way - Use remote state with locking
'''
# backend.tf
terraform {
  backend "s3" {
    bucket = "my-terraform-state"
    key    = "prod/terraform.tfstate"
    region = "us-west-2"
    
    # ๐Ÿ”’ Enable state locking
    dynamodb_table = "terraform-state-lock"
    encrypt        = true
  }
}
'''

# ๐Ÿ Python helper for safe operations
import fcntl

class SafeTerraform:
    def __init__(self):
        self.lock_file = "/tmp/terraform.lock"
    
    def acquire_lock(self):
        """๐Ÿ”’ Acquire exclusive lock"""
        self.lock_fd = open(self.lock_file, 'w')
        fcntl.flock(self.lock_fd, fcntl.LOCK_EX)
        print("๐Ÿ”’ Lock acquired, safe to proceed!")
    
    def release_lock(self):
        """๐Ÿ”“ Release lock"""
        fcntl.flock(self.lock_fd, fcntl.LOCK_UN)
        self.lock_fd.close()
        print("๐Ÿ”“ Lock released!")

๐Ÿคฏ Pitfall 2: Hardcoded Secrets

# โŒ Dangerous - Never hardcode secrets!
provider_config = '''
provider "aws" {
  access_key = "AKIAIOSFODNN7EXAMPLE"  # ๐Ÿ’ฅ Security breach!
  secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}
'''

# โœ… Safe - Use environment variables or secrets manager
import os
from typing import Dict

class SecureConfig:
    def __init__(self):
        self.secrets = {}
    
    def load_from_env(self):
        """๐Ÿ”’ Load secrets from environment"""
        required_vars = ['AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY']
        
        for var in required_vars:
            if var not in os.environ:
                raise ValueError(f"โš ๏ธ Missing required environment variable: {var}")
            self.secrets[var] = os.environ[var]
        
        print("โœ… Secrets loaded securely from environment!")
    
    def generate_provider_config(self) -> str:
        """๐Ÿ“ Generate secure provider configuration"""
        return '''
provider "aws" {
  # ๐Ÿ”’ Credentials loaded from environment or IAM role
  region = var.aws_region
}

variable "aws_region" {
  description = "AWS region for resources"
  default     = "us-west-2"
}
'''

๐Ÿ› ๏ธ Best Practices

  1. ๐ŸŽฏ Use Modules: Create reusable infrastructure components
  2. ๐Ÿ“ Version Everything: Tag your infrastructure code versions
  3. ๐Ÿ›ก๏ธ Implement State Locking: Prevent concurrent modifications
  4. ๐ŸŽจ Follow Naming Conventions: Use consistent resource naming
  5. โœจ Automate Testing: Test infrastructure changes before applying

๐Ÿงช Hands-On Exercise

๐ŸŽฏ Challenge: Build a Complete Web App Infrastructure

Create a Terraform configuration managed by Python that includes:

๐Ÿ“‹ Requirements:

  • โœ… VPC with public and private subnets
  • ๐ŸŒ Application Load Balancer
  • ๐Ÿ–ฅ๏ธ Auto Scaling Group with EC2 instances
  • ๐Ÿ—„๏ธ RDS database in private subnet
  • ๐Ÿ”’ Security groups with proper rules
  • ๐Ÿ“Š CloudWatch monitoring and alarms
  • ๐ŸŽจ Each resource needs proper tags!

๐Ÿš€ Bonus Points:

  • Add S3 bucket for static assets
  • Implement blue-green deployment capability
  • Create a disaster recovery plan

๐Ÿ’ก Solution

๐Ÿ” Click to see solution
# ๐ŸŽฏ Complete web app infrastructure solution!
import json
import os

class WebAppInfrastructure:
    def __init__(self, app_name, environment):
        self.app_name = app_name
        self.environment = environment
        self.config = {
            "provider": {},
            "resource": {},
            "variable": {},
            "output": {}
        }
    
    def add_networking(self):
        """๐ŸŒ Add VPC and networking components"""
        # VPC
        self.config["resource"]["aws_vpc"] = {
            "main": {
                "cidr_block": "10.0.0.0/16",
                "enable_dns_hostnames": True,
                "tags": {
                    "Name": f"{self.app_name}-vpc",
                    "Environment": self.environment,
                    "Emoji": "๐ŸŒ"
                }
            }
        }
        
        # Public Subnet
        self.config["resource"]["aws_subnet"] = {
            "public": {
                "vpc_id": "${aws_vpc.main.id}",
                "cidr_block": "10.0.1.0/24",
                "availability_zone": "${data.aws_availability_zones.available.names[0]}",
                "map_public_ip_on_launch": True,
                "tags": {
                    "Name": f"{self.app_name}-public-subnet",
                    "Type": "Public",
                    "Emoji": "๐ŸŒ"
                }
            },
            "private": {
                "vpc_id": "${aws_vpc.main.id}",
                "cidr_block": "10.0.2.0/24",
                "availability_zone": "${data.aws_availability_zones.available.names[1]}",
                "tags": {
                    "Name": f"{self.app_name}-private-subnet",
                    "Type": "Private",
                    "Emoji": "๐Ÿ”’"
                }
            }
        }
    
    def add_compute(self):
        """๐Ÿ–ฅ๏ธ Add compute resources"""
        # Launch Template
        self.config["resource"]["aws_launch_template"] = {
            "app": {
                "name_prefix": f"{self.app_name}-",
                "image_id": "${data.aws_ami.amazon_linux_2.id}",
                "instance_type": "t3.micro",
                "user_data": "${base64encode(file(\"${path.module}/user_data.sh\"))}",
                "vpc_security_group_ids": ["${aws_security_group.app.id}"],
                "tag_specifications": [{
                    "resource_type": "instance",
                    "tags": {
                        "Name": f"{self.app_name}-instance",
                        "Environment": self.environment,
                        "Emoji": "๐Ÿ–ฅ๏ธ"
                    }
                }]
            }
        }
        
        # Auto Scaling Group
        self.config["resource"]["aws_autoscaling_group"] = {
            "app": {
                "name": f"{self.app_name}-asg",
                "min_size": 2,
                "max_size": 10,
                "desired_capacity": 3,
                "health_check_type": "ELB",
                "health_check_grace_period": 300,
                "launch_template": {
                    "id": "${aws_launch_template.app.id}",
                    "version": "$Latest"
                },
                "vpc_zone_identifier": ["${aws_subnet.public.id}"],
                "target_group_arns": ["${aws_lb_target_group.app.arn}"],
                "tag": [{
                    "key": "Name",
                    "value": f"{self.app_name}-asg-instance",
                    "propagate_at_launch": True
                }]
            }
        }
    
    def add_database(self):
        """๐Ÿ—„๏ธ Add RDS database"""
        self.config["resource"]["aws_db_instance"] = {
            "main": {
                "identifier": f"{self.app_name}-db",
                "engine": "postgres",
                "engine_version": "13.7",
                "instance_class": "db.t3.micro",
                "allocated_storage": 20,
                "storage_encrypted": True,
                "db_name": self.app_name.replace("-", "_"),
                "username": "dbadmin",
                "password": "${random_password.db_password.result}",
                "vpc_security_group_ids": ["${aws_security_group.db.id}"],
                "db_subnet_group_name": "${aws_db_subnet_group.main.name}",
                "skip_final_snapshot": self.environment != "production",
                "tags": {
                    "Name": f"{self.app_name}-database",
                    "Environment": self.environment,
                    "Emoji": "๐Ÿ—„๏ธ"
                }
            }
        }
        
        # Random password for DB
        self.config["resource"]["random_password"] = {
            "db_password": {
                "length": 16,
                "special": True,
                "override_special": "!#$%&*()-_=+[]{}<>:?"
            }
        }
    
    def add_monitoring(self):
        """๐Ÿ“Š Add CloudWatch monitoring"""
        self.config["resource"]["aws_cloudwatch_metric_alarm"] = {
            "high_cpu": {
                "alarm_name": f"{self.app_name}-high-cpu",
                "comparison_operator": "GreaterThanThreshold",
                "evaluation_periods": "2",
                "metric_name": "CPUUtilization",
                "namespace": "AWS/EC2",
                "period": "120",
                "statistic": "Average",
                "threshold": "80",
                "alarm_description": "This metric monitors ec2 cpu utilization",
                "alarm_actions": ["${aws_sns_topic.alerts.arn}"],
                "dimensions": {
                    "AutoScalingGroupName": "${aws_autoscaling_group.app.name}"
                },
                "tags": {
                    "Name": f"{self.app_name}-cpu-alarm",
                    "Emoji": "๐Ÿšจ"
                }
            }
        }
    
    def generate_terraform(self):
        """๐Ÿ“ Generate complete Terraform configuration"""
        # Add all components
        self.add_networking()
        self.add_compute()
        self.add_database()
        self.add_monitoring()
        
        # Convert to HCL format
        tf_content = []
        
        # Provider
        tf_content.append('''
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
  }
}

provider "aws" {
  region = var.aws_region
}

# ๐Ÿ“ Data sources
data "aws_availability_zones" "available" {
  state = "available"
}

data "aws_ami" "amazon_linux_2" {
  most_recent = true
  owners      = ["amazon"]
  
  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-ebs"]
  }
}
''')
        
        # Variables
        tf_content.append(f'''
variable "aws_region" {{
  description = "AWS region for resources"
  default     = "us-west-2"
}}

variable "app_name" {{
  description = "Application name"
  default     = "{self.app_name}"
}}

variable "environment" {{
  description = "Environment name"
  default     = "{self.environment}"
}}
''')
        
        return '\n'.join(tf_content)

# ๐ŸŽฎ Test the infrastructure generator
infra = WebAppInfrastructure("awesome-app", "development")
terraform_config = infra.generate_terraform()

# ๐Ÿ’พ Save configuration
with open("main.tf", "w") as f:
    f.write(terraform_config)

print("โœ… Complete web app infrastructure generated!")
print("๐Ÿ“‹ Next steps:")
print("  1. Run 'terraform init' to initialize")
print("  2. Run 'terraform plan' to review changes")
print("  3. Run 'terraform apply' to create infrastructure")
print("  4. Celebrate! ๐ŸŽ‰")

๐ŸŽ“ Key Takeaways

Youโ€™ve learned so much! Hereโ€™s what you can now do:

  • โœ… Create Infrastructure as Code with confidence ๐Ÿ’ช
  • โœ… Avoid common Terraform mistakes that trip up beginners ๐Ÿ›ก๏ธ
  • โœ… Apply best practices in real projects ๐ŸŽฏ
  • โœ… Debug infrastructure issues like a pro ๐Ÿ›
  • โœ… Build awesome infrastructure with Terraform and Python! ๐Ÿš€

Remember: Infrastructure as Code is your friend, not your enemy! Itโ€™s here to help you build reliable, repeatable infrastructure. ๐Ÿค

๐Ÿค Next Steps

Congratulations! ๐ŸŽ‰ Youโ€™ve mastered Terraform and Infrastructure as Code!

Hereโ€™s what to do next:

  1. ๐Ÿ’ป Practice with the exercises above
  2. ๐Ÿ—๏ธ Build infrastructure for your own project
  3. ๐Ÿ“š Move on to our next tutorial: Kubernetes Basics
  4. ๐ŸŒŸ Share your infrastructure automation journey with others!

Remember: Every DevOps expert was once a beginner. Keep coding, keep learning, and most importantly, have fun! ๐Ÿš€


Happy infrastructure coding! ๐ŸŽ‰๐Ÿš€โœจ