Configuring S3 Object Lock for Compliance Spatial Archives
Configuring S3 Object Lock for compliance spatial archives means enforcing Write-Once-Read-Many immutability at the storage layer so regulated geospatial records — environmental baselines, cadastral surveys, defense deliverables — cannot be altered or deleted until a retention clock expires, with legal holds that override any lifecycle rule. This guide is for compliance and infrastructure teams who must produce a legally defensible answer to “prove this survey has not been modified” and cannot rely on application-layer controls that a misconfiguration or a compromised credential could bypass. The subtle decisions are choosing COMPLIANCE versus GOVERNANCE mode correctly, setting a default retention that matches the governing mandate without over-locking, and wiring legal holds so an active hold suspends the lifecycle transitions and expiries described elsewhere in your archive. Get the mode wrong and you either cannot delete data you legally must purge, or you can delete data you legally must keep.
How a Delete Request Is Evaluated
Object Lock is a stack of independent controls — a legal hold, a retention clock, and a retention mode — each of which can independently block a mutation. Understanding the evaluation order is the whole game:
Choosing the Retention Mode
The mode decision is irreversible in one direction and legally consequential, so make it deliberately. GOVERNANCE mode blocks deletions and overwrites for ordinary callers but lets a principal holding the s3:BypassGovernanceRetention permission remove the lock — appropriate for internal data-management policies where an administrator must retain a correction path. COMPLIANCE mode blocks everyone, including the root account, until the retain-until date passes; no principal, no permission, and no support ticket can shorten it. Regulatory records that demand tamper-proof custody — the kind an auditor will test — belong in COMPLIANCE. Operational retention that merely enforces internal discipline belongs in GOVERNANCE.
Two constraints shape the rollout. Object Lock can only be enabled on a bucket at creation time (or via support for existing versioned buckets), and it requires versioning. Enforcing Retention Policy Frameworks therefore starts with the bucket definition itself, not a policy bolted on later.
Provisioning the Lock in Terraform
Codify the bucket, its default retention, and the mode so the archive’s immutability posture is reproducible from source and auditable through pull requests. Map the days directly to the governing regulatory window.
resource "aws_s3_bucket" "spatial_archive" {
bucket = "spatial-archive"
object_lock_enabled = true
}
resource "aws_s3_bucket_versioning" "spatial_archive" {
bucket = aws_s3_bucket.spatial_archive.id
versioning_configuration { status = "Enabled" }
}
resource "aws_s3_bucket_object_lock_configuration" "compliance" {
bucket = aws_s3_bucket.spatial_archive.id
rule {
default_retention {
mode = "COMPLIANCE"
days = 3650 # 10-year environmental-record retention mandate
}
}
}
Default retention stamps every newly uploaded object with a retain-until date computed from its upload time — a survey landed today under this configuration is immutable until 2036. Override per object only to lengthen, never to shorten, and record the governing mandate in object metadata so the retention window is self-documenting. These immutable objects are the evidentiary backstop beneath the lifecycle transitions in Implementing Lifecycle Rules for Shapefile Archives: a rule may move an object to GLACIER, but Object Lock guarantees it cannot vanish mid-window.
The Order Operations Must Happen In
Object Lock has one property that catches teams out: it can only be enabled on a bucket at creation. Everything else about retention is per object and adjustable, but the container-level switch is not, and discovering that after a petabyte has landed means copying the whole archive into a new bucket.
The corollary is to enable Object Lock on any bucket that might one day hold retained data, even if no default rule is set. The switch costs nothing when unused: objects written into a lock-enabled bucket with no default rule and no per-object retention behave exactly like ordinary objects. What it buys is the ability to start retaining without a migration.
Applying Legal Holds and Regulatory Mapping
A legal hold is an on/off flag independent of the retention clock. It blocks deletion for as long as it is set, even after the retain-until date passes, which is exactly what a litigation hold on a disputed parcel record requires:
# Place an indefinite legal hold on a specific survey record
aws s3api put-object-legal-hold --bucket spatial-archive \
--key survey/2019/environmental_baseline.gpkg \
--legal-hold '{"Status":"ON"}'
# Set a longer per-object retention than the bucket default (extend only)
aws s3api put-object-retention --bucket spatial-archive \
--key survey/2019/environmental_baseline.gpkg \
--retention '{"Mode":"COMPLIANCE","RetainUntilDate":"2040-01-01T00:00:00Z"}'
Map each dataset class to the window its mandate dictates rather than applying one blanket duration: a ten-year environmental baseline, a permanent cadastral record held under indefinite legal hold, a seven-year contract deliverable. When a retention window finally expires and deletion is authorized, sanitize according to NIST SP 800-88 Rev 1, treating KMS key destruction as the cloud-native equivalent of media destruction. The CRS lineage and provenance those records carry — asserted upstream by CRS Synchronization in Pipelines — are part of the legally significant content the lock protects, not incidental metadata.
Two interactions catch teams out when they extend a lock across a real archive. First, replication and Object Lock must be designed together: to keep an immutable copy in a second region, the destination bucket must itself have Object Lock enabled, and same-mode replication carries the retain-until date across so the replica is protected identically — a replica without its own lock is a deletable back door around the whole scheme. Second, retroactive locking of an existing archive is a batch operation, not a bucket flag. Objects that predate the lock configuration carry no retention, so apply retention to them explicitly with an S3 Batch Operations job that issues put-object-retention across the inventory, and verify coverage against a bucket inventory report rather than assuming the default retention reached historical uploads.
Minimum-duration billing is the cost tail of any retention decision. A ten-year COMPLIANCE lock means those objects cannot leave their storage class early without penalty, so the retention window and the tier policy have to agree — locking an object into COMPLIANCE while a lifecycle rule tries to expire it produces a rule that silently no-ops for a decade. Price the locked footprint over its full window, since the commitment is contractual the moment the object lands.
Verifying Immutability
A lock you have not tested is a lock you do not have. Confirm the configuration, inspect an object’s retention, then attempt a deletion and confirm it is refused.
aws s3api get-object-lock-configuration --bucket spatial-archive \
--query "ObjectLockConfiguration.Rule.DefaultRetention"
Expected output — the mode and window match the mandate:
{
"Mode": "COMPLIANCE",
"Days": 3650
}
Now prove a delete is actually refused on a locked object:
aws s3api delete-object --bucket spatial-archive \
--key survey/2019/environmental_baseline.gpkg \
--version-id "3sL4kqtJlcpXroDTDmJ+rmSpXd3dIbrHY"
Expected result — the storage layer, not the application, refuses:
An error occurred (AccessDenied) when calling the DeleteObject operation:
Access Denied because object protected by object lock.
An AccessDenied citing object lock is the proof auditors want: the immutability is enforced below any credential. If the delete succeeds, either versioning is disabled, the object predates the lock configuration, or the retain-until date has already passed — each of which is a finding to remediate before certifying the archive.
Reading Lock State Across the Archive
Retention status is a per-object-version property, which means the question “is this dataset protected?” has no single answer — it has one answer per object. Auditing it at scale requires the inventory report rather than per-object API calls.
The last row is the finding that matters, and it is almost always explicable: objects written before the default rule was set, objects copied in by a job that overrode the default, or objects whose write path bypasses the pipeline. Each of those is a process defect rather than a storage one, and finding them is the whole point of running the audit against the inventory rather than assuming the default rule covered everything.
Diagnosing Lock Failures
| Symptom | Root Cause | Resolution |
|---|---|---|
| Object deletes despite lock config | Object uploaded before Object Lock was enabled, or no per-object retention applied | Re-copy objects post-configuration; confirm default retention stamps new uploads |
| Cannot enable Object Lock on existing bucket | Lock requires enablement at creation or via support, plus versioning | Create a new locked bucket and migrate; enable versioning first |
| Retention cannot be shortened after error | COMPLIANCE mode forbids shortening even for root | Accept the window; use GOVERNANCE only where a correction path is legally acceptable |
| Object expired but must stay | Retention lapsed with no legal hold set | Apply a legal hold, which overrides expiry until explicitly removed |
Operational Execution Checklist
Frequently Asked Questions
What happens to a locked object when its lifecycle rule wants to expire it?
The expiry is refused for as long as the retention date is in the future or a legal hold is active. The rule is not an error and does not stop; it simply cannot delete that version, and it will try again. This is the intended interaction — retention overrides lifecycle — but it does mean the lifecycle configuration is not a reliable statement of what will actually be deleted, so deletion reporting has to come from the inventory rather than from the rule.
Can retention be applied retroactively to existing objects?
Per object, yes — a retain-until date can be set on an existing object version in a lock-enabled bucket, and extended thereafter. What cannot be done retroactively is enabling the feature on a bucket that was created without it. That is the distinction worth remembering when planning: the per-object work is scriptable, the bucket-level work is a migration.
How should the retention date be chosen for spatial data with no explicit mandate?
From the data’s role rather than from its size. Reference layers that other datasets are derived from usually warrant the longest terms, because losing them invalidates the derivatives; intermediate products warrant the shortest. Where no rule exists, a documented default per collection is better than per-object judgement, and reviewing those defaults annually is cheaper than reviewing individual objects ever.
Related
- Up: Retention Policy Frameworks — the parent reference for legally defensible retention across the spatial archive.
- Implementing Lifecycle Rules for Shapefile Archives — the lifecycle transitions that Object Lock guarantees cannot delete data mid-window.
- How to Design a 3-Tier Spatial Storage Architecture — the tier model whose cold destinations these locked objects occupy.
- Automating CRS Transformations in ETL Pipelines — asserting the CRS lineage that forms part of the locked record’s legal content.
- Spatial Archive Cost Modeling — accounting for the minimum-duration billing that locked retention windows imply.
Part of the Spatial Data Archival knowledge base.