AWS
03 / 03

S3, Lambda & IAM Patterns

S3, Lambda & IAM Patterns

S3 — Key Operations (AWS CLI)

# Create / manage buckets
aws s3 mb s3://my-bucket
aws s3 rb s3://my-bucket --force

# Copy / sync
aws s3 cp ./dist s3://my-bucket/dist/ --recursive
aws s3 cp s3://my-bucket/file.txt ./local/
aws s3 sync ./dist s3://my-bucket/ --delete  # delete files not in source
aws s3 sync s3://src-bucket s3://dst-bucket  # bucket-to-bucket

# List / delete
aws s3 ls s3://my-bucket/
aws s3 rm s3://my-bucket/old-file.txt
aws s3 rm s3://my-bucket/logs/ --recursive

# Presigned URL (temporary access)
aws s3 presign s3://my-bucket/private-file.pdf --expires-in 3600

# Set public static website hosting
aws s3 website s3://my-bucket/ --index-document index.html --error-document error.html

# S3 bucket policy (public read)
{
  "Version": "2012-10-17",
  "Statement": [{"Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::my-bucket/*"}]
}

Lambda

// Lambda handler (Node.js)
export const handler = async (event, context) => {
  console.log('Event:', JSON.stringify(event, null, 2));

  // API Gateway event
  const { httpMethod, path, queryStringParameters, body } = event;
  const data = body ? JSON.parse(body) : null;

  // S3 event
  const bucket = event.Records[0].s3.bucket.name;
  const key = decodeURIComponent(event.Records[0].s3.object.key);

  // SQS event
  for (const record of event.Records) {
    const message = JSON.parse(record.body);
    await processMessage(message);
  }

  return {
    statusCode: 200,
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ message: 'Success' }),
  };
};

// Environment variables
const db = process.env.DATABASE_URL;

// AWS SDK v3
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3';
const s3 = new S3Client({ region: 'us-east-1' });
const obj = await s3.send(new GetObjectCommand({ Bucket: 'my-bucket', Key: 'file.txt' }));

IAM — Roles & Policies

// IAM Policy — allow Lambda to read S3 and write CloudWatch Logs
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:ListBucket"],
      "Resource": [
        "arn:aws:s3:::my-bucket",
        "arn:aws:s3:::my-bucket/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:*:*:*"
    }
  ]
}

// Trust policy — who can assume this role
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": { "Service": "lambda.amazonaws.com" },
    "Action": "sts:AssumeRole"
  }]
}

Cost Optimization Tips

  • Use Reserved Instances or Savings Plans for predictable workloads (up to 72% off On-Demand)

  • Use Spot Instances for fault-tolerant batch workloads (up to 90% off)

  • S3 lifecycle policies — move old data to S3-IA or Glacier

  • Enable S3 Intelligent-Tiering for unpredictable access patterns

  • CloudFront reduces S3 data transfer costs (CloudFront egress is cheaper)

  • Right-size EC2 instances using AWS Compute Optimizer recommendations

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free