Skip to main content

NTC Backup

Release Notes Implementation Blueprint 🔒   Source Code

Description​

NTC Backup provisions a dedicated, central backup account that orchestrates AWS Backup across your AWS Organization through AWS Organizations BACKUP_POLICY documents, instead of configuring AWS Backup separately in every member account.

Member accounts back up resources locally first for fast operational recovery, then optionally copy the same recovery point into this account's central vault for durability and isolation from the source account. Backup Vault Lock can additionally give a central vault WORM-style immutability once its grace period expires.

Running backup centrally from its own account keeps recovery points out of reach of a compromised or accidentally deleted workload account: the central vault policy grants member accounts exactly one permission, backup:CopyIntoBackupVault, so even a fully compromised workload account has no path to read, restore, or delete a recovery point once it has landed here. This separation is also what frameworks like ISO 27001 and FINMA expect from backup and recovery: demonstrably independent of the systems it protects.

Getting started

NTC Backup doesn't work standalone - see Architecture Review below for how it depends on NTC Account Factory and NTC Organizations, and the Guide to Configure NTC Backup for the full setup, step by step.

See Backup Policies for how policy attachment, tag-based resource selection, and cross-account copy behave once configured, Member Account Requirements for what individual resources additionally need, Restore for how to bring a recovery point back, Malware Protection for scanning recovery points with Amazon GuardDuty, and Troubleshooting for known issues.

Architecture Review​

NTC Backup spans three separate Terraform stacks, each owned by a different account, each doing a distinct job. Understanding how they relate is the fastest way to understand the whole system - and the most common source of confusion when something doesn't work.

Management Account: Grants the Right to Govern​

The Organizations management account doesn't touch backups directly. It delegates AWS Backup administration to the backup account - registering it as delegated admin, granting it rights to manage BACKUP_POLICY documents, and turning on org-wide cross-account backup. This is largely a one-time setup step; it doesn't need to change as backup_definitions evolves.

Backup Account: Authors the Policy​

The backup account is the delegated administrator granted above. For each backup_definitions entry it owns a central vault and KMS key, holding recovery points copied in from member accounts, isolated from the accounts that produced them. It also authors the actual BACKUP_POLICY document - what gets backed up, on what schedule, with what retention - and attaches it to target OUs/accounts via the Organizations API.

Member Accounts: Run the Backups​

The backup baseline template, rolled out via NTC Account Factory, provisions a local vault and the IAM roles AWS Backup and GuardDuty need in every account you want backed up. This is where backup jobs actually run: each job backs up a resource into the local vault first, then optionally copies that same recovery point into the backup account's central vault. This is also the only place actual backup plans exist as real AWS resources.

How the Three Fit Together​

The management account grants the right to govern. The backup account decides what gets backed up and where centrally. AWS Backup then carries out that decision in each member account, using whatever the baseline template already set up there.

If the baseline template is missing, or its vault/role names don't match what the backup account expects, jobs fail with AccessDenied - even though the other two pieces are correct. This is why the Guide to Configure NTC Backup sets the three stacks up in that exact order.

NTC Backup Architecture

Usage​

Latest Release-
Loading usage example from blueprint...

Requirements​

The following requirements are needed by this module:

  • terraform (>= 1.5.7)

  • aws (>= 6.0)

Providers​

The following providers are used by this module:

  • aws (>= 6.0)

Modules​

No modules.

Resources​

The following resources are used by this module:

Required Inputs​

No required inputs.

Optional Inputs​

The following input variables are optional (have default values):

backup_definitions​

Description: One entry per central backup vault (+ KMS key, tag-driven backup plan pair, and its own AWS
Organizations BACKUP_POLICY document). Entries are keyed by name, which is used to generate all resource names and must be unique.
Each entry's backup plan is attached to the specified OUs and/or account IDs

  • name: unique identifier - drives every resource name this entry creates (vault, KMS alias,
    plan/selection names). Renaming recreates them all.
  • region: region for this entry's central vault + KMS key.
  • resource_types: AWS services eligible for backup. Valid values are:
    EC2, EBS, RDS, Aurora, Neptune, DocumentDB, DynamoDB, EFS, S3, FSx,
    CloudFormation, Redshift, Redshift Serverless, SAP HANA on Amazon EC2,
    Storage Gateway, Timestream, EKS
  • copy_to_backup_definition_by_name: other entries' names to ALSO copy this entry's
    local-and-central backups to, beyond its own vault.
  • local_backup_vault_retention_days / central_backup_vault_retention_days: retention (days) in
    the local (member account) vault / this entry's central vault.
  • cron_schedule: backup schedule for both the local-only and local-and-central plans - cron(...)
    only, hourly-or-coarser (AWS Backup rejects finer). Defaults to every 4 hours.
  • backup_start_tolerance_minutes: minutes AWS Backup waits for a scheduled job to start before
    marking it missed.
  • central_vault_lock_config: Backup Vault Lock for the CENTRAL vault only (the local vault is
    never locked by this module).
    • enabled: turns the lock on.
    • min_retention_days / max_retention_days: the range every rule's retention must fall within
      once locked - central_backup_vault_retention_days must fall inside it too, or AWS rejects
      the plan.
    • changeable_for_days: grace period before the lock becomes PERMANENT and these bounds can
      never be adjusted again.
  • member_account_backup_role_name: IAM role name in member accounts that AWS Backup assumes
    to back up resources.
  • backup_target_ou_path_ids: OU path IDs (no trailing "/*") to attach this entry's policy to and
    trust for copy-in.
  • backup_target_account_ids: account IDs to attach + trust directly, independent of OU
    membership. At least one of this or backup_target_ou_path_ids is required.
  • tag_key_to_enable_backup / tag_key_to_define_backup_scope: tag keys driving whether a resource
    is backed up at all, and whether it's also copied centrally.
  • backup_enabled_if_untagged: whether an untagged resource is backed up by default.
  • default_backup_scope: default scope ("local-only" or "local-and-central") for untagged
    resources while tag_based_selection_enabled is true; the STATIC scope for every resource once
    it's false.
  • tag_based_selection_enabled: whether tag-based per-resource selection is active. false turns
    default_backup_scope into a static, entry-wide switch instead of a default.

Type:

list(object({
name = string
region = string
resource_types = optional(list(string), ["EC2", "EBS", "RDS", "Aurora", "Neptune", "DocumentDB", "DynamoDB", "EFS", "S3"])
copy_to_backup_definition_by_name = optional(list(string), [])
local_backup_vault_retention_days = optional(number, 7)
central_backup_vault_retention_days = optional(number, 30)
cron_schedule = optional(string, "cron(30 0/4 ? * * *)")
backup_start_tolerance_minutes = optional(number, 60)
central_vault_lock_config = optional(object({
enabled = optional(bool, false)
min_retention_days = optional(number, 10)
max_retention_days = optional(number, 90)
changeable_for_days = optional(number, 30) # grace period before the lock becomes PERMANENT
}), {})
member_account_backup_role_name = optional(string, "ntc-local-backup-operator-role")
backup_target_ou_path_ids = optional(list(string), []) # full OU path IDs, no trailing "/*"
backup_target_account_ids = optional(list(string), []) # attach + trust directly, independent of OU membership
tag_key_to_enable_backup = optional(string, "ntc:backup")
tag_key_to_define_backup_scope = optional(string, "ntc:backup-scope")
backup_enabled_if_untagged = optional(bool, false)
default_backup_scope = optional(string, "local-and-central")
tag_based_selection_enabled = optional(bool, true)
}))

Default: []

central_backup_operator_iam_role_name​

Description: Name of the IAM role in THIS (central backup) account that AWS Backup assumes to manage backups
within the central vaults and receive copies from member accounts. Shared across every
backup_definitions entry.

Type: string

Default: "ntc-central-backup-operator-role"

rds_service_linked_role_enabled​

Description: Whether to create AWSServiceRoleForRDS, required before AWS Backup can copy an RDS-family
recovery point into a vault here. Set to false if it already exists (EntityAlreadyExists
otherwise) - import it instead: tofu import aws_iam_service_linked_role.ntc_rds_service_linked_role arn:aws:iam::<ACCOUNT_ID>:role/aws-service-role/rds.amazonaws.com/AWSServiceRoleForRDS.

Type: bool

Default: true

Outputs​

The following outputs are exported:

backup_vault_arns​

Description: ARNs of the central backup vaults, keyed by backup_definitions entry name.

backup_vault_kms_key_arns​

Description: ARNs of the KMS keys used for backup encryption, keyed by backup_definitions entry name.

backup_vault_names​

Description: Names of the central backup vaults, keyed by backup_definitions entry name.