/Amazon S3 for Developers
Concept
Medium

Amazon S3 for Developers

12 min read·S3StorageSecurityDVA-C02

A comprehensive deep dive into Amazon S3 for application developers — consistency model, storage classes, versioning, multipart upload, pre-signed URLs, event notifications, encryption options, access control, lifecycle policies, replication, and DVA-C02 exam essentials.


What is Amazon S3?

Amazon S3 (Simple Storage Service) is an object store — not a file system or block device. Objects are stored in buckets, retrieved by key, and each object can be up to 5 TB. S3 is designed for 11 nines (99.999999999%) of durability by replicating data across multiple AZs.

Core mental model: S3 is a flat key-value store. The "folders" you see in the console are just key prefixes (e.g., images/profile/alice.jpg — the full key). There is no real directory hierarchy.


Consistency Model

Since December 2020, S3 provides strong read-after-write consistency for all operations:

OperationBefore Dec 2020Now
PUT new object → GETEventual (possible stale 404)Strongly consistent
PUT overwrite → GETEventualStrongly consistent
DELETE → GETEventualStrongly consistent
LIST after PUTEventualStrongly consistent

No extra configuration needed — strong consistency is the default for all buckets.


Storage Classes

ClassAvailabilityMin storageMin durationUse case
S3 Standard99.99%NoneNoneFrequently accessed data
S3 Intelligent-Tiering99.9%NoneNoneUnknown/changing access patterns
S3 Standard-IA99.9%128 KB30 daysInfrequently accessed, rapid retrieval
S3 One Zone-IA99.5%128 KB30 daysIA data, loss-tolerant (single AZ)
S3 Glacier Instant99.9%128 KB90 daysArchive, millisecond retrieval
S3 Glacier Flexible99.99%40 KB90 daysArchive, minutes-to-hours retrieval
S3 Glacier Deep Archive99.99%40 KB180 daysLong-term archive, 12h retrieval
S3 Express One Zone99.95%NoneNoneUltra-low latency (single AZ, new)

All classes except One Zone-IA and Express One Zone replicate across ≥ 3 AZs.


Object Operations

Basic CRUD

javascript
1import {
2  S3Client, PutObjectCommand, GetObjectCommand,
3  DeleteObjectCommand, CopyObjectCommand, HeadObjectCommand,
4  ListObjectsV2Command,
5} from '@aws-sdk/client-s3';
6
7const s3 = new S3Client({ region: 'us-east-1' });
8
9// ── PUT ───────────────────────────────────────────────────────────────────────
10await s3.send(new PutObjectCommand({
11  Bucket: 'my-bucket',
12  Key: 'uploads/profile/alice.jpg',
13  Body: fileBuffer,
14  ContentType: 'image/jpeg',
15  Metadata: { 'uploaded-by': 'alice', 'source': 'mobile-app' },
16  StorageClass: 'STANDARD_IA',
17  ServerSideEncryption: 'aws:kms',
18  SSEKMSKeyId: 'alias/my-key',
19  Tagging: 'env=prod&team=platform',
20}));
21
22// ── GET ───────────────────────────────────────────────────────────────────────
23const { Body, ContentType, ContentLength } = await s3.send(new GetObjectCommand({
24  Bucket: 'my-bucket',
25  Key: 'uploads/profile/alice.jpg',
26  // Range: 'bytes=0-1023',  // byte-range fetch (first 1 KB)
27}));
28const buffer = Buffer.from(await Body.transformToByteArray());
29
30// ── HEAD (metadata without body) ─────────────────────────────────────────────
31const { ContentLength: size, LastModified, Metadata } = await s3.send(
32  new HeadObjectCommand({ Bucket: 'my-bucket', Key: 'uploads/profile/alice.jpg' })
33);
34
35// ── COPY ─────────────────────────────────────────────────────────────────────
36await s3.send(new CopyObjectCommand({
37  CopySource: 'source-bucket/original/file.txt',
38  Bucket: 'dest-bucket',
39  Key: 'copies/file.txt',
40  StorageClass: 'GLACIER',
41}));
42
43// ── LIST (paginated) ─────────────────────────────────────────────────────────
44let continuationToken;
45do {
46  const { Contents, NextContinuationToken } = await s3.send(
47    new ListObjectsV2Command({
48      Bucket: 'my-bucket',
49      Prefix: 'uploads/',
50      MaxKeys: 1000,
51      ContinuationToken: continuationToken,
52    })
53  );
54  for (const obj of Contents ?? []) {
55    console.log(obj.Key, obj.Size, obj.LastModified);
56  }
57  continuationToken = NextContinuationToken;
58} while (continuationToken);

Multipart Upload

Multipart upload splits large files into parts that upload in parallel:

Rendering diagram…
RuleValue
Recommended forObjects > 100 MB
Required forObjects > 5 GB
Max object size5 TB
Min part size5 MB (except the last part)
Max parts10,000
Incomplete uploadsMust call AbortMultipartUpload or use lifecycle rule to auto-clean
javascript
1import { Upload } from '@aws-sdk/lib-storage';
2import { createReadStream } from 'fs';
3
4// High-level Upload class handles multipart automatically
5const upload = new Upload({
6  client: s3,
7  params: {
8    Bucket: 'my-bucket',
9    Key: 'large-file.zip',
10    Body: createReadStream('/tmp/large-file.zip'),
11    ContentType: 'application/zip',
12  },
13  partSize: 10 * 1024 * 1024,   // 10 MB per part
14  queueSize: 4,                  // 4 concurrent part uploads
15});
16
17upload.on('httpUploadProgress', ({ loaded, total }) => {
18  console.log(`Uploaded ${loaded} / ${total} bytes`);
19});
20
21await upload.done();

Pre-signed URLs

Pre-signed URLs grant temporary access to a specific S3 object without requiring AWS credentials on the client.

javascript
1import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
2
3// ── PRE-SIGNED GET (download) ─────────────────────────────────────────────────
4const downloadUrl = await getSignedUrl(s3, new GetObjectCommand({
5  Bucket: 'my-bucket',
6  Key: 'reports/invoice-2024-01.pdf',
7  ResponseContentDisposition: 'attachment; filename="invoice.pdf"',
8}), { expiresIn: 3600 });   // 1 hour
9
10// ── PRE-SIGNED PUT (direct browser upload) ────────────────────────────────────
11const uploadUrl = await getSignedUrl(s3, new PutObjectCommand({
12  Bucket: 'my-bucket',
13  Key: `uploads/${userId}/${filename}`,
14  ContentType: 'image/jpeg',
15}), { expiresIn: 300 });   // 5 minutes
16
17// Client uses uploadUrl with a plain HTTP PUT — no AWS credentials needed
18// fetch(uploadUrl, { method: 'PUT', body: file, headers: { 'Content-Type': 'image/jpeg' } })
Signed byMax expiry
IAM user (long-term credentials)7 days
IAM role / STS credentials12 hours (capped by STS session duration)
EC2 Instance Profile6 hours

Gotcha: Pre-signed URLs expire when the signing credentials expire — even if the URL's own expiry hasn't been reached. An instance profile token that expires in 1 hour will invalidate a pre-signed URL that was set to 6 hours.


S3 Event Notifications

S3 can trigger actions when objects are created, deleted, restored, or replicated.

Rendering diagram…
javascript
1// Lambda triggered by S3 ObjectCreated event
2export async function handler(event) {
3  for (const record of event.Records) {
4    const bucket = record.s3.bucket.name;
5    const key = decodeURIComponent(record.s3.object.key.replace(/\+/g, ' '));
6    const size = record.s3.object.size;
7    const eventName = record.eventName;   // e.g., 'ObjectCreated:Put'
8
9    console.log(`${eventName}: s3://${bucket}/${key} (${size} bytes)`);
10    await processUpload(bucket, key);
11  }
12}
Event typeTriggers when
s3:ObjectCreated:*PUT, POST, COPY, CompleteMultipartUpload
s3:ObjectRemoved:*DELETE, DeleteMarkerCreated
s3:ObjectRestore:*Glacier restore initiated or completed
s3:Replication:*Replication failure, missed threshold
s3:LifecycleExpiration:*Lifecycle rule deleted an object

S3 → EventBridge is preferred when you need: content-based filtering, multiple targets per event, archiving, or replay.


Versioning

Rendering diagram…
OperationResult
PUT objectNew version created; previous version retained
GET object (no version)Returns AWSCURRENT (latest)
GET object + VersionIdReturns specific version
DELETE object (no version)Inserts delete marker; previous versions still exist
DELETE specific VersionIdPermanently removes that version
DELETE the delete markerRestores the object (previous version becomes current)
bash
1# List all versions of an object
2aws s3api list-object-versions   --bucket my-bucket   --prefix photos/alice.jpg
3
4# Restore a previous version (copy it to become current)
5aws s3 cp s3://my-bucket/photos/alice.jpg   s3://my-bucket/photos/alice.jpg   --version-id abc123
6
7# Permanently delete a specific version
8aws s3api delete-object   --bucket my-bucket   --key photos/alice.jpg   --version-id abc123

MFA Delete: Requires MFA token to permanently delete object versions or change bucket versioning state. Must be enabled by the root account.


Encryption Options

MethodKey managed byAPI call per objectHeader
SSE-S3S3 (AES-256)Nox-amz-server-side-encryption: AES256
SSE-KMSKMS CMKYes (GenerateDataKey)x-amz-server-side-encryption: aws:kms
DSSE-KMSKMS CMK (dual layer)Yesx-amz-server-side-encryption: aws:kms:dsse
SSE-CYou (per request)Nox-amz-server-side-encryption-customer-key
Client-sideYou (before upload)Optional KMSNone
bash
1# Enforce encryption at rest — deny unencrypted PUTs via bucket policy
2aws s3api put-bucket-policy --bucket my-bucket --policy '{
3  "Version": "2012-10-17",
4  "Statement": [{
5    "Sid": "DenyUnencryptedObjectUploads",
6    "Effect": "Deny",
7    "Principal": "*",
8    "Action": "s3:PutObject",
9    "Resource": "arn:aws:s3:::my-bucket/*",
10    "Condition": {
11      "StringNotEquals": {
12        "s3:x-amz-server-side-encryption": "aws:kms"
13      }
14    }
15  }]
16}'
17
18# Enforce HTTPS-only access
19aws s3api put-bucket-policy --bucket my-bucket --policy '{
20  "Statement": [{
21    "Effect": "Deny",
22    "Principal": "*",
23    "Action": "s3:*",
24    "Resource": ["arn:aws:s3:::my-bucket", "arn:aws:s3:::my-bucket/*"],
25    "Condition": { "Bool": { "aws:SecureTransport": "false" } }
26  }]
27}'

Access Control

Public Access Block

Always-on protection at the bucket or account level:

bash
1# Block ALL public access on a bucket (recommended default)
2aws s3api put-public-access-block   --bucket my-bucket   --public-access-block-configuration     BlockPublicAcls=true,IgnorePublicAcls=true,    BlockPublicPolicy=true,RestrictPublicBuckets=true

Bucket Policies vs ACLs vs IAM

Bucket PolicyIAM PolicyACL
TypeResource-basedIdentity-basedLegacy per-object/bucket
Cross-account✅ direct✅ via role✅ (legacy)
Conditions✅ full✅ full
Recommended❌ (disable ACLs)

Cross-Account Access Pattern

json
1{
2  "Version": "2012-10-17",
3  "Statement": [{
4    "Sid": "AllowCrossAccountRead",
5    "Effect": "Allow",
6    "Principal": { "AWS": "arn:aws:iam::987654321098:role/DataPipelineRole" },
7    "Action": ["s3:GetObject", "s3:ListBucket"],
8    "Resource": [
9      "arn:aws:s3:::my-data-bucket",
10      "arn:aws:s3:::my-data-bucket/*"
11    ]
12  }]
13}

Lifecycle Policies

Automatically transition objects between storage classes or expire them:

json
1{
2  "Rules": [
3    {
4      "ID": "IntelligentTieringAfter30Days",
5      "Status": "Enabled",
6      "Filter": { "Prefix": "logs/" },
7      "Transitions": [
8        { "Days": 30,  "StorageClass": "INTELLIGENT_TIERING" },
9        { "Days": 90,  "StorageClass": "GLACIER" },
10        { "Days": 365, "StorageClass": "DEEP_ARCHIVE" }
11      ],
12      "Expiration": { "Days": 2555 }
13    },
14    {
15      "ID": "CleanupIncompleteMultipartUploads",
16      "Status": "Enabled",
17      "Filter": {},
18      "AbortIncompleteMultipartUpload": { "DaysAfterInitiation": 7 }
19    },
20    {
21      "ID": "ExpireOldVersions",
22      "Status": "Enabled",
23      "Filter": {},
24      "NoncurrentVersionExpiration": { "NoncurrentDays": 30 },
25      "NoncurrentVersionTransitions": [
26        { "NoncurrentDays": 7, "StorageClass": "STANDARD_IA" }
27      ]
28    }
29  ]
30}

S3 Replication

Rendering diagram…
CRRSRR
RegionDifferent regionSame region
Use caseDR, latency, complianceLog aggregation, test/prod sync
Versioning required✅ both buckets✅ both buckets
Delete markers replicatedOptionalOptional
Existing objectsNot replicated by default (use S3 Batch Operations)Same

Replication requires: versioning enabled on both source and destination, and an IAM role with permissions to read source and write destination.


S3 Select & Glacier Select

S3 Select uses SQL to filter data inside S3 — only the matching rows are transferred:

javascript
1import { SelectObjectContentCommand } from '@aws-sdk/client-s3';
2
3const response = await s3.send(new SelectObjectContentCommand({
4  Bucket: 'my-bucket',
5  Key: 'data/orders-2024.csv',
6  ExpressionType: 'SQL',
7  Expression: "SELECT * FROM S3Object WHERE status = 'failed' AND amount > 1000",
8  InputSerialization: {
9    CSV: { FileHeaderInfo: 'USE', RecordDelimiter: '\n', FieldDelimiter: ',' },
10    CompressionType: 'GZIP',
11  },
12  OutputSerialization: { CSV: {} },
13}));
14
15// Stream the filtered results
16for await (const event of response.Payload) {
17  if (event.Records?.Payload) {
18    process.stdout.write(Buffer.from(event.Records.Payload));
19  }
20}

Supported formats: CSV, JSON, Parquet. Supports GZIP and BZIP2 compression.


CORS Configuration

CORS is required when a browser makes direct S3 requests from a different origin:

json
1[
2  {
3    "AllowedHeaders": ["*"],
4    "AllowedMethods": ["GET", "PUT", "POST", "DELETE", "HEAD"],
5    "AllowedOrigins": ["https://app.example.com"],
6    "ExposeHeaders": ["ETag", "x-amz-request-id"],
7    "MaxAgeSeconds": 3000
8  }
9]
bash
1aws s3api put-bucket-cors   --bucket my-bucket   --cors-configuration file://cors.json

When you need CORS: Browser JavaScript calls S3 directly (e.g., pre-signed PUT for direct upload). Not needed when your backend proxies S3 requests.


Transfer Acceleration & Other Performance Features

FeatureWhat it doesURL / config
Transfer AccelerationRoutes uploads via CloudFront edge → AWS backbonebucket.s3-accelerate.amazonaws.com
Byte-range fetchDownload specific bytes in parallel (multipart-style download)Range: bytes=0-1048575 header
Requester PaysRequester pays data transfer costs (not bucket owner)Bucket setting
S3 Express One ZoneSingle-AZ, sub-millisecond latency for ML/HPC workloadsDifferent bucket type (--create-bucket-configuration LocationConstraint)
bash
1# Enable Transfer Acceleration
2aws s3api put-bucket-accelerate-configuration   --bucket my-bucket   --accelerate-configuration Status=Enabled
3
4# Upload via accelerated endpoint
5aws s3 cp large-file.zip s3://my-bucket/   --endpoint-url https://my-bucket.s3-accelerate.amazonaws.com

Object Lock (WORM)

Object Lock prevents objects from being deleted or overwritten — used for compliance and ransomware protection.

Rendering diagram…
ModeOverride possible?Who can override
Compliance❌ No oneNo one (not even root or AWS)
GovernanceUsers with s3:BypassGovernanceRetention permission
Legal HoldUsers with s3:PutObjectLegalHold permission

DVA-C02 Quick Reference

TopicKey Fact
S3 consistencyStrong read-after-write for all operations (since Dec 2020)
Max object size5 TB
Max single PUT5 GB (use multipart for larger)
Multipart recommendedObjects > 100 MB
Multipart requiredObjects > 5 GB
Pre-signed URL max (IAM user)7 days
Pre-signed URL max (IAM role)12 hours (STS session cap)
SSE-S3 headerx-amz-server-side-encryption: AES256
SSE-KMS headerx-amz-server-side-encryption: aws:kms
SSE-KMS KMS callGenerateDataKey per object
SSE-C requirementHTTPS mandatory
Versioning DELETECreates delete marker — does not remove versions
MFA DeleteRequires MFA to permanently delete versions
S3 Event notification targetsSQS, SNS, Lambda, EventBridge
Lifecycle transition min duration30 days in Standard before moving to Standard-IA
Replication requiresVersioning on both source and destination buckets
Existing objects replicatedNo — use S3 Batch Operations
S3 Select formatsCSV, JSON, Parquet
Transfer Acceleration URLbucket.s3-accelerate.amazonaws.com
Object Lock Compliance modeNo one can delete before retention (not even root)
Object Lock Governance overrides3:BypassGovernanceRetention permission
CORS purposeBrowser direct S3 requests from different origin
S3 Express One ZoneSub-millisecond latency, single AZ, ML/HPC workloads

Practice Questions6

easy

Q1. A developer needs to allow a web browser to make cross-origin requests to an S3 bucket to display images. The browser shows a CORS error. What must the developer configure?


Select one answer before revealing.

medium

Q2. A developer uses the S3 SDK to upload 100 MB files. Uploads frequently fail on slow connections. What S3 feature should the developer use to improve reliability?


Select one answer before revealing.

easy

Q3. A developer needs to generate a temporary URL allowing an unauthenticated user to download a specific S3 object for exactly 15 minutes. Which S3 feature should be used?


Select one answer before revealing.

easy

Q4. A developer accidentally deleted 1,000 S3 objects. The bucket does not have versioning enabled. Can the objects be recovered?


Select one answer before revealing.

medium

Q5. A developer needs to store sensitive medical records in S3. The data must be encrypted with a customer-managed key, and all key usage must be auditable. The developer also needs to rotate the key annually. Which S3 encryption configuration satisfies all requirements?


Select one answer before revealing.

easy

Q6. A developer needs to trigger a Lambda function every time a new object is uploaded to an S3 bucket. Which S3 feature should be configured?


Select one answer before revealing.