This post explains how small businesses can configure cloud storage to meet NIST SP 800-171 Rev.2 / CMMC 2.0 Level 2 control SC.L2-3.13.16 — which requires using cryptographic mechanisms to protect Controlled Unclassified Information (CUI) at rest — and includes actionable Terraform examples and policy snippets for AWS, Azure, and Google Cloud.
Understanding SC.L2-3.13.16: objectives and practical scope
SC.L2-3.13.16 requires that organizations apply cryptographic controls to protect the confidentiality of CUI when it is stored. For small businesses this means: identify all cloud buckets/volumes that may hold CUI, ensure server-side or client-side encryption is enabled with appropriate key management, enforce encryption policy automatically (deny unencrypted uploads), protect and audit keys, and document processes for key rotation and access control. The goal is not only to encrypt data, but to ensure encryption is consistently applied, auditable, and tied to strong key management policies.
Practical cloud configuration patterns
AWS: S3 + KMS + bucket policy + Terraform
Common pattern: enable SSE-KMS on S3 buckets, create a dedicated KMS key with key rotation enabled, restrict KMS key usage via a tight key policy, and add a bucket policy that denies PutObject if encryption headers are missing or not using the required algorithm. Example Terraform resources below demonstrate these elements for a small business that wants one KMS key per environment and denies unencrypted uploads.
# AWS: create KMS key and S3 bucket with default encryption and deny unencrypted uploads
resource "aws_kms_key" "cui" {
description = "CUI encryption key for production S3"
enable_key_rotation = true
deletion_window_in_days = 30
}
resource "aws_s3_bucket" "cui" {
bucket = "acme-cui-prod-bucket"
acl = "private"
force_destroy = false
server_side_encryption_configuration {
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = aws_kms_key.cui.key_id
}
}
}
}
# Bucket policy: deny PutObject if the request does not specify SSE-KMS
resource "aws_s3_bucket_policy" "deny_unencrypted" {
bucket = aws_s3_bucket.cui.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "DenyUnEncryptedObjectUploads"
Effect = "Deny"
Principal = "*"
Action = "s3:PutObject"
Resource = "${aws_s3_bucket.cui.arn}/*"
Condition = {
StringNotEquals = {
"s3:x-amz-server-side-encryption" = "aws:kms"
}
}
}
]
})
}
Azure: Storage Account with customer-managed key (CMK) in Key Vault
Use an Azure Key Vault key as a customer-managed key (CMK) for storage account encryption and enable system-assigned identity so the storage account can access the key. Enforce HTTPS-only and enable diagnostic logs for auditability. This Terraform snippet shows the core pieces; for production add RBAC on the Key Vault and soft-delete/ purge protection.
# Azure: Key Vault key and storage account using CMK
resource "azurerm_key_vault" "kv" {
name = "acme-cui-kv"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
tenant_id = data.azurerm_client_config.current.tenant_id
sku_name = "standard"
soft_delete_enabled = true
purge_protection_enabled = true
}
resource "azurerm_key_vault_key" "cui_key" {
name = "cui-storage-key"
key_vault_id = azurerm_key_vault.kv.id
key_type = "RSA"
key_size = 2048
}
resource "azurerm_storage_account" "cui" {
name = "acmecuistorage"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
account_tier = "Standard"
account_replication_type = "GRS"
enable_https_traffic_only = true
identity {
type = "SystemAssigned"
}
encryption {
services {
blob = { enabled = true }
file = { enabled = true }
queue = { enabled = true }
}
key_source = "Microsoft.Keyvault"
key_vault_key_id = azurerm_key_vault_key.cui_key.id
}
}
GCP: Cloud Storage with CMEK and Terraform
For Google Cloud, use Cloud KMS keys (CMEK) and set the bucket's default_kms_key_name so all new objects are encrypted with the CMEK. Also enable Bucket Lock and uniform bucket-level access for predictable ACL behavior and enable audit logs. The example below assumes you already have a KMS key ring.
# GCP: KMS CryptoKey and GCS bucket with default KMS key
resource "google_kms_crypto_key" "cui_key" {
name = "cui-key"
key_ring = google_kms_key_ring.ring.id
rotation_period = "7776000s" # 90 days
}
resource "google_storage_bucket" "cui" {
name = "acme-cui-prod-bucket"
location = "US"
uniform_bucket_level_access = true
force_destroy = false
encryption {
default_kms_key_name = google_kms_crypto_key.cui_key.id
}
}
Compliance tips and best practices for small businesses
Actionable tips: (1) Create an inventory of buckets/disks and tag those that may hold CUI; (2) Enforce encryption in IaC (Terraform) and block unencrypted resource creation via policies or pipeline guardrails (e.g., Terraform checks, tfsec, pre-commit hooks, or cloud-native policy engines); (3) Use customer-managed keys (CMK/CMEK) where feasible so you control rotation and access; (4) Harden key policies: enforce least privilege, restrict principals and services that can use the key, and require separate IAM roles for key administration vs. day-to-day use; (5) Enable audit logging (CloudTrail, Azure Monitor, GCP Audit Logs) and S3/Blob access logging so you can prove encryption and access patterns for assessments.
Real-world scenarios and small-business examples
Example 1: A small engineering firm storing specification PDFs for DoD subcontractors. Tag buckets as "CUI", enforce SSE-KMS, and set Terraform plan checks so developers cannot create unencrypted buckets. Example 2: A vendor that shares CUI with partners — implement per-partner KMS keys and key usage policies, rotate keys quarterly (or enforced rotation), and require key approval workflows for granting decryption rights. Example 3: Legacy backups on object storage — run an inventory and migrate non-encrypted objects into new encrypted buckets using a batch job that reads and re-writes objects under the correct keys, then disable public access and add retention rules.
Risks of not implementing SC.L2-3.13.16 controls
Failing to implement these controls exposes CUI to unauthorized disclosure and increases risk of data breaches, contract noncompliance, loss of DoD or federal contracts, fines, and reputational damage. Operationally, unencrypted or inconsistently encrypted storage makes incident response and forensic validation harder: you can't prove data was protected, and you may be required to notify customers and regulators. From a security perspective, weak key management (shared accounts, no rotation, broad key policies) effectively nullifies encryption.
In summary, meeting SC.L2-3.13.16 for NIST SP 800-171 Rev.2 / CMMC 2.0 Level 2 is achievable for small businesses by combining strong key management, enforcing encryption through Terraform and cloud-native policies, and operationalizing audit and rotation processes. Use the provided Terraform patterns as a baseline, integrate policy checks into CI/CD, document key administration and rotation procedures, and regularly audit your inventory so CUI stays encrypted and auditable at rest.