{
  "title": "How to Configure Cloud Storage to Protect CUI at Rest: Terraform and Policy Examples for NIST SP 800-171 REV.2 / CMMC 2.0 Level 2 - Control - SC.L2-3.13.16",
  "date": "2026-04-13",
  "author": "Lakeridge Technologies",
  "featured_image": "/assets/images/blog/2026/4/how-to-configure-cloud-storage-to-protect-cui-at-rest-terraform-and-policy-examples-for-nist-sp-800-171-rev2-cmmc-20-level-2-control-scl2-31316.jpg",
  "content": {
    "full_html": "<p>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.</p>\n\n<h2>Understanding SC.L2-3.13.16: objectives and practical scope</h2>\n<p>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.</p>\n\n<h2>Practical cloud configuration patterns</h2>\n<h3>AWS: S3 + KMS + bucket policy + Terraform</h3>\n<p>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.</p>\n<pre><code># AWS: create KMS key and S3 bucket with default encryption and deny unencrypted uploads\nresource \"aws_kms_key\" \"cui\" {\n  description             = \"CUI encryption key for production S3\"\n  enable_key_rotation     = true\n  deletion_window_in_days = 30\n}\n\nresource \"aws_s3_bucket\" \"cui\" {\n  bucket = \"acme-cui-prod-bucket\"\n  acl    = \"private\"\n  force_destroy = false\n\n  server_side_encryption_configuration {\n    rule {\n      apply_server_side_encryption_by_default {\n        sse_algorithm     = \"aws:kms\"\n        kms_master_key_id = aws_kms_key.cui.key_id\n      }\n    }\n  }\n}\n\n# Bucket policy: deny PutObject if the request does not specify SSE-KMS\nresource \"aws_s3_bucket_policy\" \"deny_unencrypted\" {\n  bucket = aws_s3_bucket.cui.id\n  policy = jsonencode({\n    Version = \"2012-10-17\"\n    Statement = [\n      {\n        Sid       = \"DenyUnEncryptedObjectUploads\"\n        Effect    = \"Deny\"\n        Principal = \"*\"\n        Action    = \"s3:PutObject\"\n        Resource  = \"${aws_s3_bucket.cui.arn}/*\"\n        Condition = {\n          StringNotEquals = {\n            \"s3:x-amz-server-side-encryption\" = \"aws:kms\"\n          }\n        }\n      }\n    ]\n  })\n}\n</code></pre>\n\n<h3>Azure: Storage Account with customer-managed key (CMK) in Key Vault</h3>\n<p>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.</p>\n<pre><code># Azure: Key Vault key and storage account using CMK\nresource \"azurerm_key_vault\" \"kv\" {\n  name                = \"acme-cui-kv\"\n  location            = azurerm_resource_group.rg.location\n  resource_group_name = azurerm_resource_group.rg.name\n  tenant_id           = data.azurerm_client_config.current.tenant_id\n  sku_name            = \"standard\"\n  soft_delete_enabled = true\n  purge_protection_enabled = true\n}\n\nresource \"azurerm_key_vault_key\" \"cui_key\" {\n  name         = \"cui-storage-key\"\n  key_vault_id = azurerm_key_vault.kv.id\n  key_type     = \"RSA\"\n  key_size     = 2048\n}\n\nresource \"azurerm_storage_account\" \"cui\" {\n  name                     = \"acmecuistorage\"\n  resource_group_name      = azurerm_resource_group.rg.name\n  location                 = azurerm_resource_group.rg.location\n  account_tier             = \"Standard\"\n  account_replication_type = \"GRS\"\n  enable_https_traffic_only = true\n\n  identity {\n    type = \"SystemAssigned\"\n  }\n\n  encryption {\n    services {\n      blob  = { enabled = true }\n      file  = { enabled = true }\n      queue = { enabled = true }\n    }\n    key_source     = \"Microsoft.Keyvault\"\n    key_vault_key_id = azurerm_key_vault_key.cui_key.id\n  }\n}\n</code></pre>\n\n<h3>GCP: Cloud Storage with CMEK and Terraform</h3>\n<p>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.</p>\n<pre><code># GCP: KMS CryptoKey and GCS bucket with default KMS key\nresource \"google_kms_crypto_key\" \"cui_key\" {\n  name            = \"cui-key\"\n  key_ring        = google_kms_key_ring.ring.id\n  rotation_period = \"7776000s\" # 90 days\n}\n\nresource \"google_storage_bucket\" \"cui\" {\n  name                        = \"acme-cui-prod-bucket\"\n  location                    = \"US\"\n  uniform_bucket_level_access = true\n  force_destroy               = false\n\n  encryption {\n    default_kms_key_name = google_kms_crypto_key.cui_key.id\n  }\n}\n</code></pre>\n\n<h2>Compliance tips and best practices for small businesses</h2>\n<p>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.</p>\n\n<h2>Real-world scenarios and small-business examples</h2>\n<p>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.</p>\n\n<h2>Risks of not implementing SC.L2-3.13.16 controls</h2>\n<p>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.</p>\n\n<p>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.</p>",
    "plain_text": "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.\n\nUnderstanding SC.L2-3.13.16: objectives and practical scope\nSC.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.\n\nPractical cloud configuration patterns\nAWS: S3 + KMS + bucket policy + Terraform\nCommon 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.\n# AWS: create KMS key and S3 bucket with default encryption and deny unencrypted uploads\nresource \"aws_kms_key\" \"cui\" {\n  description             = \"CUI encryption key for production S3\"\n  enable_key_rotation     = true\n  deletion_window_in_days = 30\n}\n\nresource \"aws_s3_bucket\" \"cui\" {\n  bucket = \"acme-cui-prod-bucket\"\n  acl    = \"private\"\n  force_destroy = false\n\n  server_side_encryption_configuration {\n    rule {\n      apply_server_side_encryption_by_default {\n        sse_algorithm     = \"aws:kms\"\n        kms_master_key_id = aws_kms_key.cui.key_id\n      }\n    }\n  }\n}\n\n# Bucket policy: deny PutObject if the request does not specify SSE-KMS\nresource \"aws_s3_bucket_policy\" \"deny_unencrypted\" {\n  bucket = aws_s3_bucket.cui.id\n  policy = jsonencode({\n    Version = \"2012-10-17\"\n    Statement = [\n      {\n        Sid       = \"DenyUnEncryptedObjectUploads\"\n        Effect    = \"Deny\"\n        Principal = \"*\"\n        Action    = \"s3:PutObject\"\n        Resource  = \"${aws_s3_bucket.cui.arn}/*\"\n        Condition = {\n          StringNotEquals = {\n            \"s3:x-amz-server-side-encryption\" = \"aws:kms\"\n          }\n        }\n      }\n    ]\n  })\n}\n\n\nAzure: Storage Account with customer-managed key (CMK) in Key Vault\nUse 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.\n# Azure: Key Vault key and storage account using CMK\nresource \"azurerm_key_vault\" \"kv\" {\n  name                = \"acme-cui-kv\"\n  location            = azurerm_resource_group.rg.location\n  resource_group_name = azurerm_resource_group.rg.name\n  tenant_id           = data.azurerm_client_config.current.tenant_id\n  sku_name            = \"standard\"\n  soft_delete_enabled = true\n  purge_protection_enabled = true\n}\n\nresource \"azurerm_key_vault_key\" \"cui_key\" {\n  name         = \"cui-storage-key\"\n  key_vault_id = azurerm_key_vault.kv.id\n  key_type     = \"RSA\"\n  key_size     = 2048\n}\n\nresource \"azurerm_storage_account\" \"cui\" {\n  name                     = \"acmecuistorage\"\n  resource_group_name      = azurerm_resource_group.rg.name\n  location                 = azurerm_resource_group.rg.location\n  account_tier             = \"Standard\"\n  account_replication_type = \"GRS\"\n  enable_https_traffic_only = true\n\n  identity {\n    type = \"SystemAssigned\"\n  }\n\n  encryption {\n    services {\n      blob  = { enabled = true }\n      file  = { enabled = true }\n      queue = { enabled = true }\n    }\n    key_source     = \"Microsoft.Keyvault\"\n    key_vault_key_id = azurerm_key_vault_key.cui_key.id\n  }\n}\n\n\nGCP: Cloud Storage with CMEK and Terraform\nFor 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.\n# GCP: KMS CryptoKey and GCS bucket with default KMS key\nresource \"google_kms_crypto_key\" \"cui_key\" {\n  name            = \"cui-key\"\n  key_ring        = google_kms_key_ring.ring.id\n  rotation_period = \"7776000s\" # 90 days\n}\n\nresource \"google_storage_bucket\" \"cui\" {\n  name                        = \"acme-cui-prod-bucket\"\n  location                    = \"US\"\n  uniform_bucket_level_access = true\n  force_destroy               = false\n\n  encryption {\n    default_kms_key_name = google_kms_crypto_key.cui_key.id\n  }\n}\n\n\nCompliance tips and best practices for small businesses\nActionable 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.\n\nReal-world scenarios and small-business examples\nExample 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.\n\nRisks of not implementing SC.L2-3.13.16 controls\nFailing 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.\n\nIn 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."
  },
  "metadata": {
    "description": "Practical, step-by-step Terraform and policy examples to enforce cryptographic protection of CUI at rest for NIST SP 800-171 Rev.2 / CMMC 2.0 Level 2 compliance.",
    "permalink": "/how-to-configure-cloud-storage-to-protect-cui-at-rest-terraform-and-policy-examples-for-nist-sp-800-171-rev2-cmmc-20-level-2-control-scl2-31316.json",
    "categories": [],
    "tags": []
  }
}