DEV Community

Engr.Hamza
Engr.Hamza

Posted on

Provision S3 State Bucket and DynamoDB Lock Table

Cover Image

{
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.

Enter fullscreen mode Exit fullscreen mode


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.

Enter fullscreen mode Exit fullscreen mode


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
Enter fullscreen mode Exit fullscreen mode

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.")
Enter fullscreen mode Exit fullscreen mode

---

## 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.

Enter fullscreen mode Exit fullscreen mode


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.

Enter fullscreen mode Exit fullscreen mode


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"/>
Enter fullscreen mode Exit fullscreen mode

Top comments (0)