Skip to content

S3 buckets should have MFA delete enabled

Without MFA Delete, anyone with sufficient IAM permissions can permanently remove object versions or suspend versioning. A compromised credential or overly permissive policy can cause irreversible data loss with no recovery path. MFA Delete adds a second authentication factor that blocks automated or unauthorized deletion attempts, including those from valid but compromised credentials.

This matters most for buckets storing backups, audit logs, or regulated data where tamper-proof retention is required. Ransomware commonly targets versioning and delete operations to eliminate recovery options, and MFA Delete is one of the few controls that actually stops that pattern.

Retrofit consideration

MFA Delete can only be enabled by the root account using the AWS CLI with an MFA token. Terraform cannot enable it autonomously because the API call requires live MFA credentials. Retrofitting existing buckets requires a manual, root-level operation per bucket.

Implementation

Choose the approach that matches how you manage Terraform.

Use the compliance.tf module to enforce this control by default. See get started with compliance.tf.

module "s3_bucket" {
  source  = "pcidss.compliance.tf/terraform-aws-modules/s3-bucket/aws"
  version = ">=5.0.0"

  bucket = "abc123"
}

module "s3_bucket" {
  source  = "cis.compliance.tf/terraform-aws-modules/s3-bucket/aws"
  version = ">=5.0.0"

  bucket = "abc123"
}

module "s3_bucket" {
  source  = "cisv500.compliance.tf/terraform-aws-modules/s3-bucket/aws"
  version = ">=5.0.0"

  bucket = "abc123"
}

module "s3_bucket" {
  source  = "acscessentialeight.compliance.tf/terraform-aws-modules/s3-bucket/aws"
  version = ">=5.0.0"

  bucket = "abc123"
}

module "s3_bucket" {
  source  = "cisv120.compliance.tf/terraform-aws-modules/s3-bucket/aws"
  version = ">=5.0.0"

  bucket = "abc123"
}

module "s3_bucket" {
  source  = "nistcsfv11.compliance.tf/terraform-aws-modules/s3-bucket/aws"
  version = ">=5.0.0"

  bucket = "abc123"
}

If you use terraform-aws-modules/s3-bucket/aws, set the right module inputs for this control. You can later migrate to the compliance.tf module with minimal changes because it is compatible by design.

module "s3_bucket" {
  source  = "terraform-aws-modules/s3-bucket/aws"
  version = ">=5.0.0"

  bucket = "abc123"

  versioning = {
    mfa_delete = "Enabled"
  }
}

Use AWS provider resources directly. See docs for the resources involved: aws_s3_bucket_versioning.

resource "aws_s3_bucket" "this" {
  bucket        = "pofix-abc123"
  force_destroy = true
}

resource "aws_s3_bucket_versioning" "this" {
  bucket = "example-bucket-abc123"
  versioning_configuration {
    mfa_delete = "Enabled"
    status     = "Enabled"
  }
}

What this control checks

This control validates that both versioning and MFA Delete are enabled on the bucket. It fails when versioning is suspended or absent, or when MFA Delete is not enabled.

In AWS, enabling or disabling MFA Delete requires a PutBucketVersioning request authenticated as the root account and including a valid MFA code. Because of this root-plus-MFA requirement, many teams configure MFA Delete out-of-band using aws s3api put-bucket-versioning and treat it as an externally managed setting in Terraform.

Common pitfalls

  • Root account required for enablement

    The PutBucketVersioning API call with MFA Delete requires root account credentials and a valid MFA token. IAM users and roles cannot enable MFA Delete regardless of their permissions. Terraform runs using IAM roles will fail to apply this setting, producing an AccessDenied error.

  • Terraform cannot fully manage MFA Delete lifecycle

    Terraform cannot manage MFA Delete through normal provider workflows. Changing MFA Delete requires a root-authenticated request with a current MFA code, which automated pipelines cannot produce. Enable it via aws s3api put-bucket-versioning in the CLI, then exclude that setting from Terraform state management entirely.

  • Disabling MFA Delete also requires MFA

    Once enabled, MFA Delete cannot be disabled without root account access and a valid MFA token. If root MFA access is lost, you cannot revert this setting. Document and test root MFA recovery procedures before enabling.

  • Versioning must also be enabled

    MFA Delete only applies when versioning is active. If versioning_configuration.status is set to "Suspended" or omitted, MFA Delete has no practical effect even if the flag appears in the API response.

Audit evidence

An auditor expects to see the output of aws s3api get-bucket-versioning --bucket <bucket-name> showing both "Status": "Enabled" and "MFADelete": "Enabled" for each in-scope bucket. AWS Config rules such as s3-bucket-versioning-enabled provide continuous compliance evaluation results, though a custom Config rule or third-party scanner specifically flagging MFA Delete status is stronger evidence.

CloudTrail logs for PutBucketVersioning events should confirm MFA Delete was configured, with the x-amz-mfa header present in the request. Tools like Prowler or Steampipe that enumerate MFA Delete status across all buckets work as periodic attestation artifacts.

Framework-specific interpretation

PCI DSS v4.0: Requirement 8.4 calls for MFA on all access into the cardholder data environment. For buckets storing cardholder data, MFA Delete means that even a fully authenticated principal with broad S3 permissions cannot permanently destroy object versions without a second factor, which is the exact assurance Requirement 8 is after.

Tool mappings

Use these identifiers to cross-reference this control across tools, reports, and evidence.

  • Compliance.tf Control: s3_bucket_mfa_delete_enabled

  • AWS Config Managed Rule: S3_BUCKET_MFA_DELETE_ENABLED

  • Powerpipe Control: aws_compliance.control.s3_bucket_mfa_delete_enabled

  • Prowler Checks: cloudtrail_bucket_requires_mfa_delete, s3_bucket_no_mfa_delete

  • AWS Security Hub Control: S3.20

  • KICS Query: c5b31ab9-0f26-4a49-b8aa-4cc064392f4d

  • Trivy Check: AWS-0170

Last reviewed: 2026-03-09