{
region = var.aws_region
default_tags {
tags = {
Environment = var.environment
ManagedBy = "Terraform"
Workshop = "Cloud-Day1-Session1"
}
}
}
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.1.0"
name = "${var.environment}-main-vpc"
cidr = "10.0.0.0/16"
azs = ["${var.aws_region}a", "${var.aws_region}b", "${var.aws_region}c"]
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
single_nat_gateway = var.environment != "production"
enable_dns_hostnames = true
enable_dns_support = true
}
This declarative definition isolates public-facing ingress points from internal microservices while maintaining reproducible, version-controlled networking layers across multiple cloud environments.
---
## Let's Build It — Step by Step
Building a scalable cloud environment requires systematic execution across three distinct layers: remote state security, network topology, and workload deployment pipelines.
### Step 1: Remote State Locking with DynamoDB and S3
Before provisioning compute assets, secure the Terraform remote state backend to support safe concurrent access and prevent state corruption.
bash
!/usr/bin/env bash
set -euo pipefail
Provision S3 State Bucket and DynamoDB Lock Table
AWS_REGION="us-east-1"
BUCKET_NAME="cloud-workshop-tf-state-prod"
DYNAMO_TABLE="cloud-workshop-tf-locks"
echo "Creating secure S3 bucket for Terraform state storage..."
aws s3api create-bucket \
--bucket "${BUCKET_NAME}" \
--region "${AWS_REGION}"
aws s3api put-bucket-encryption \
--bucket "${BUCKET_NAME}" \
--server-side-encryption-configuration '{
"Rules": [{"ApplyServerSideEncryptionByDefault": {"SSEAlgorithm": "AES256"}}]
}'
aws s3api put-bucket-versioning \
--bucket "${BUCKET_NAME}" \
--versioning-configuration Status=Enabled
echo "Creating DynamoDB state locking table..."
aws dynamodb create-table \
--table-name "${DYNAMO_TABLE}" \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--billing-mode PAY_PER_REQUEST \
--region "${AWS_REGION}"
echo "State storage backend configured successfully."
### Step 2: Automated Workload Deployment and Health Validation
With network foundations secured, automate application builds using Python orchestration scripts that perform integration testing prior to traffic routing.
python
!/usr/bin/env python3
import os
import sys
import time
import requests
import boto3
class CloudDeploymentPipeline:
def init(self, cluster_name: str, service_name: str, region: str = "us-east-1"):
self.ecs_client = boto3.client('ecs', region_name=region)
self.cluster_name = cluster_name
self.service_name = service_name
def trigger_deployment(self, force_new_deployment: bool = True):
print(f"Triggering deployment for service: {self.service_name}")
response = self.ecs_client.update_service(
cluster=self.cluster_name,
service=self.service_name,
forceNewDeployment=force_new_deployment
)
return response['service']['deployments'][0]['id']
def verify_health_endpoint(self, health_url: str, max_retries: int = 10, delay: int = 15) -> bool:
print(f"Validating service status at: {health_url}")
for attempt in range(1, max_retries + 1):
try:
response = requests.get(health_url, timeout=5)
if response.status_code == 200 and response.json().get("status") == "healthy":
print(f"Health verification succeeded on attempt {attempt}.")
return True
except requests.RequestException as err:
print(f"Attempt {attempt}/{max_retries} failed: {err}")
time.sleep(delay)
return False
if name == "main":
pipeline = CloudDeploymentPipeline(cluster_name="prod-cluster", service_name="api-service")
deploy_id = pipeline.trigger_deployment()
target_health_url = "https://api.production.internal/health"
if not pipeline.verify_health_endpoint(target_health_url):
print("Deployment health verification failed. Escalating to rollbacks.", file=sys.stderr)
sys.exit(1)
print("Pipeline execution completed successfully.")
---
## Why This Changes Everything
Adopting fully declarative infrastructure combined with automated verification fundamentally shifts operational capabilities:
1. **Zero-Touch Provisioning:** Infrastructure and workloads boot up deterministically from source control commits without operator intervention.
2. **Rapid Disaster Recovery:** Complete multi-region environments can be reprovisioned from clean state definitions in under 20 minutes.
3. **Immutable Infrastructure Patterns:** Servers and containers are never updated in-place; they are replaced cleanly with newly built, fully validated artifacts.
4. **Shift-Left Security Enforcement:** Static code analysis scans IaC templates for misconfigurations before code enters main git branches.
---
## Common Mistakes That Kill Your Setup
Even experienced teams run into recurring traps when scaling cloud automation pipelines. Address these vulnerabilities before promoting configurations to production.
python
Unsafe Security Configuration Example vs. Remediated Code
BANNED: Over-privileged, unvalidated configuration parser
def parse_environment_unsafe(config_file: str):
import json
with open(config_file, 'r') as f:
# DANGER: Directly injecting environment secrets without sanitization
config = json.load(f)
os.environ["DATABASE_URL"] = config["db_uri"] # Exposes credentials in process space
RECOMMENDED: Explicit Secrets Retrieval with Least-Privilege IAM Integration
def fetch_secret_secure(secret_name: str, region_name: str = "us-east-1") -> str:
session = boto3.session.Session()
client = session.client(service_name='secretsmanager', region_name=region_name)
try:
get_secret_value_response = client.get_secret_value(SecretId=secret_name)
return get_secret_value_response['SecretString']
except Exception as e:
print(f"Failed to retrieve secret securely: {e}")
raise e
### Critical Pitfalls to Avoid
* **Hardcoding API Keys and Connection Strings:** Storing credentials inside source repositories exposes systems to immediate compromise.
* **Bypassing Remote State Locks:** Running local Terraform updates without lock verification introduces concurrent state corruption.
* **Ignoring Resource Quotas:** Launching automated auto-scaling groups without setting maximum instance caps leads to budget overruns during unexpected traffic spikes.
---
## Don't Ship Until You've Done This
Before deploying to production, enforce strict quality gates across your IaC repos and CI/CD pipelines using this verification script.
bash
!/usr/bin/env bash
set -euo pipefail
echo "== [1/4] Running Terraform Format Verification =="
terraform fmt -check -recursive
echo "== [2/4] Initializing and Validating Terraform Modules =="
terraform init -backend=false
terraform validate
echo "== [3/4] Running Security Scan via TICS/Trivy =="
if command -v trivy &> /dev/null; then
trivy config . --severity HIGH,CRITICAL
else
echo "Warning: Trivy scanner not found. Skipping static security scan."
fi
echo "== [4/4] Verifying IAM Least Privilege Policies =="
python3 -c "
import json, sys
with open('policies/iam_policy.json') as f:
policy = json.load(f)
for statement in policy.get('Statement', []):
if statement.get('Effect') == 'Allow' and '' in statement.get('Action', []):
print('CRITICAL: Wildcard () action detected in policy!', file=sys.stderr)
sys.exit(1)
print('IAM policies passed security scan.')
"
echo "All pre-flight production checks passed!"
---
## Advanced Patterns for Production
Once core automation pipelines are operational, advance your cloud platform with these enterprise patterns:
* **GitOps Continuous Deployment:** Synchronize cluster state directly with git repositories using operators like ArgoCD or Flux, making git the single source of truth for both code and infrastructure.
* **Ephemeral Preview Environments:** Dynamically spawn complete, isolated environment stacks for every pull request, running integration tests before tearing them down upon merge.
* **Chaos Engineering Integrations:** Inject controlled latency and node failure events directly into non-production clusters to continuously test self-healing resilience.
---
## The Bottom Line
Day 1, Session 1 sets a clear precedent for cloud architecture: manual changes are bugs, configurations must be version-controlled, and security checks must be automated.
* **Treat Infrastructure as Code:** Replace manual web console tweaks with version-controlled, peer-reviewed code commits.
* **Enforce Immutable State Storage:** Lock remote state files using centralized storage backends to prevent concurrent corruption.
* **Implement Continuous Validation:** Integrate security scanning, policy enforcement, and health-check verifications into every deployment pipeline.
---
*Engr. Hamza | AI & MLOps Engineer | Building autonomous systems at the edge of possibility*
<FollowUp label="Want to dive into Day 1 Session 2 on Kubernetes Cluster Provisioning?" query="Proceed to Cloud Workshop Day 1 Session 2: Deep Dive into Automated Kubernetes Cluster Provisioning with Terraform and ArgoCD"/>

Top comments (0)