Skip to content

Latest commit

 

History

72 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

IBM Backup & Recovery for IKS/ROKS with Data Source Connector

Stable (With quality checks) latest release pre-commit Renovate enabled semantic-release Terraform Registry

This module deploys the Data Source Connector (DSC) via Helm into an IBM Kubernetes Service (IKS) or Red Hat OpenShift on IBM Cloud cluster, registers the cluster with IBM Backup & Recovery Service, and creates a configurable protection policy.

It automates:

  • Security group rules for DSC-to-BRS communication
  • Helm deployment of the DSC chart
  • ServiceAccount + token generation
  • Cluster registration with IBM B&R
  • Flexible backup policy with incremental schedules, retention, and optional data lock (WORM)

📖 Protection Group Configuration Guide

For detailed guidance on configuring Kubernetes Protection Groups, including limitations, best practices, and working examples, see:

Protection Group Configuration Guide

Overview

terraform-ibm-iks-ocp-backup-recovery

Usage

# ############################################################################
# Init cluster config for helm
# ############################################################################

data "ibm_container_cluster_config" "cluster_config" {
  cluster_name_id = "xxxxxxxxx" # replace with cluster ID or name
}

# ############################################################################
# Config providers
# ############################################################################

provider "ibm" {
  ibmcloud_api_key = "xxxxxxxxxxxx"  # pragma: allowlist secret
}

provider "helm" {
  kubernetes {
    host                   = data.ibm_container_cluster_config.cluster_config.host
    token                  = data.ibm_container_cluster_config.cluster_config.token
    cluster_ca_certificate = data.ibm_container_cluster_config.cluster_config.ca_certificate
  }
  registries = [
    { url = "oci://icr.io", username = "iamapikey", password = var.ibmcloud_api_key } # pragma: allowlist secret
  ]
}

provider "kubernetes" {
  host                   = data.ibm_container_cluster_config.cluster_config.host
  token                  = data.ibm_container_cluster_config.cluster_config.token
  cluster_ca_certificate = data.ibm_container_cluster_config.cluster_config.ca_certificate
}

# ############################################################################
# Install DSC
# ############################################################################
module "backup_recovery" {
  source  = "terraform-ibm-modules/iks-ocp-backup-recovery/ibm"
  version = "X.Y.Z"  # Replace "X.Y.Z" with a release version to lock into a specific release
  cluster_id                    = "xxxxxxx" # replace with ID of the cluster
  cluster_resource_group_id     = "xxxxxxx" # replace with ID of the cluster resource group
  dsc_registration_token        = "xxxxxxx" # replace with Registration_token
  connection_id                 = "xxxxxxx" # replace with connection ID
  # --- B&R Instance ---
  brs_instance_guid   = "xxxxxxx" # replace with ID of the BRS instance
  brs_instance_region = var.region
  brs_endpoint_type   = "public"
  brs_tenant_id       = "xxxxxxx" # replace with tenant ID of the BRS instance
  # --- Backup Policy ---
  policy = {
    name = "daily-with-monthly-retention"
    schedule = {
      unit      = "Hours"
      frequency = 24
    }
    retention = {
      duration = 4
      unit     = "Weeks"
    }
    use_default_backup_target = true
  }
}

Single-Shot Backup and Recovery Example

This module supports automatic recovery that runs in the same terraform apply as the backup, with no manual intervention required:

module "backup_recovery" {
  source  = "terraform-ibm-modules/iks-ocp-backup-recovery/ibm"
  version = "X.Y.Z"

  # ... cluster and BRS configuration ...

  # Protection groups define what to backup
  protection_groups = [{
    name        = "production-backup"
    policy_name = "daily-retention"
    objects = [{
      name = "production-namespace"
    }]
  }]

  # Enable automatic recovery after backup completes
  enable_recovery            = true
  recovery_mode              = "same-cluster"  # or "cross-cluster"
  wait_for_backup_completion = 30  # minutes to wait for initial backup

  # Recovery operations (triggered automatically after backup)
  recoveries = [{
    name                 = "restore-production-namespace"
    snapshot_environment = "kKubernetes"
    kubernetes_params = {
      recovery_action = "RecoverNamespaces"
      objects = [{
        snapshot_id           = "latest"  # Uses latest snapshot automatically
        protection_group_name = "production-backup"
      }]
    }
  }]
}

Same-Cluster Recovery

Restore backups within the same cluster (default mode):

module "backup_recovery" {
  source  = "terraform-ibm-modules/iks-ocp-backup-recovery/ibm"
  version = "X.Y.Z"

  cluster_id                    = "my-cluster"
  cluster_resource_group_id     = "xxxx"
  # ... other required variables ...

  # Same-cluster recovery configuration
  enable_recovery            = true
  recovery_mode              = "same-cluster"
  wait_for_backup_completion = 30

  protection_groups = [{
    name        = "app-backup"
    policy_name = "daily-retention"
    objects     = [{ name = "app-namespace" }]
  }]

  recoveries = [{
    name                 = "restore-app"
    snapshot_environment = "kKubernetes"
    kubernetes_params = {
      recovery_action = "RecoverNamespaces"
      objects = [{
        protection_group_name = "app-backup"
        snapshot_id           = "latest"
      }]
    }
  }]
}

Cross-Cluster Recovery

Restore backups from a source cluster to a different target cluster:

module "backup_recovery" {
  source  = "terraform-ibm-modules/iks-ocp-backup-recovery/ibm"
  version = "X.Y.Z"

  # Source cluster (where backups are taken)
  cluster_id                = "source-cluster"
  cluster_resource_group_id = "source-rg-id"
  # ... other required variables ...

  # Cross-cluster recovery configuration
  enable_recovery                    = true
  recovery_mode                      = "cross-cluster"
  target_cluster_id                  = "target-cluster"
  target_cluster_resource_group_id   = "target-rg-id"
  wait_for_backup_completion         = 45  # May need more time for cross-cluster

  protection_groups = [{
    name        = "production-backup"
    policy_name = "daily-retention"
    objects     = [{ name = "production-namespace" }]
  }]

  recoveries = [{
    name                 = "restore-to-target-cluster"
    snapshot_environment = "kKubernetes"
    kubernetes_params = {
      recovery_action = "RecoverNamespaces"
      objects = [{
        protection_group_name = "production-backup"
        snapshot_id           = "latest"
      }]
    }
  }]
}

Important Notes for Cross-Cluster Recovery:

  • Target cluster must be pre-registered with the same BRS instance
  • Target cluster must have the Data Source Connector installed
  • Network connectivity must exist between source and target clusters
  • Ensure target cluster has sufficient resources for the restored workloads

Manual Recovery (Without Automatic Triggering)

To perform recovery manually without automatic triggering after backup:

module "backup_recovery" {
  source  = "terraform-ibm-modules/iks-ocp-backup-recovery/ibm"
  version = "X.Y.Z"

  # ... cluster configuration ...

  # Disable automatic recovery
  enable_recovery = false

  # Define recovery operations (will not execute automatically)
  recoveries = [{
    name                 = "manual-restore"
    snapshot_environment = "kKubernetes"
    kubernetes_params = {
      recovery_action = "RecoverNamespaces"
      objects = [{
        snapshot_id         = "specific-snapshot-id-here"
        protection_group_id = "pg-456"
      }]
    }
  }]
}

Then enable recovery when needed:

# Enable recovery in your terraform.tfvars
echo 'enable_recovery = true' >> terraform.tfvars
terraform apply

Required IAM Access Policies

You need the following permissions to run this module:

  • Backup & Recovery service
    • Editor platform access
  • VPC Infrastructure
    • Editor on security groups (for DSC outbound rules)
  • Kubernetes Service
    • Editor platform access

Requirements

Name Version
terraform >= 1.9.0
helm >=3.1.0, <4.0.0
ibm >= 2.2.2, < 3.0.0
kubernetes >=3.0.1, <4.0.0
time >=0.12.1, <1.0.0

Modules

Name Source Version
backup_recovery_instance terraform-ibm-modules/backup-recovery/ibm 1.12.4
brs_s2s_auth terraform-ibm-modules/s2s-auth/ibm 2.3.1
brs_vpe terraform-ibm-modules/vpe-gateway/ibm 5.3.5
crn_parser terraform-ibm-modules/common-utilities/ibm//modules/crn-parser 1.5.0
dsc_sg_rule terraform-ibm-modules/security-group/ibm v2.9.1

Resources

Name Type
helm_release.data_source_connector resource
ibm_backup_recovery.recover_snapshot resource
ibm_backup_recovery_protection_group.protection_group resource
ibm_backup_recovery_source_registration.source_registration resource
ibm_container_vpc_worker_pool.data_source_connector resource
ibm_resource_tag.cluster_brs_tag resource
kubernetes_cluster_role_binding_v1.brsagent_admin resource
kubernetes_namespace_v1.dsc_namespace resource
kubernetes_secret_v1.brsagent_token resource
kubernetes_service_account_v1.brsagent resource
terraform_data.brs_source_deregistration_wait resource
terraform_data.cancel_pg_runs resource
terraform_data.check_existing_registration resource
terraform_data.delete_auto_protect_pg resource
terraform_data.dsc_immutable_values resource
terraform_data.install_dependencies resource
terraform_data.purge_stale_dsc_pvc resource
terraform_data.trigger_backup_run resource
terraform_data.wait_before_helm_destroy resource
terraform_data.wait_for_backup_run resource
terraform_data.wait_for_dsc_node_ready resource
terraform_data.wait_for_source_discovery resource
time_sleep.wait_for_dsc_stabilization resource
time_sleep.wait_for_pg_registration resource
ibm_backup_recovery_protection_group_runs.backup_runs data source
ibm_backup_recovery_protection_sources.sources data source
ibm_container_cluster.classic_cluster data source
ibm_container_cluster_config.cluster_config data source
ibm_container_vpc_cluster.vpc_cluster data source
ibm_container_vpc_worker_pool.pool data source
ibm_iam_auth_token.source_account data source
ibm_iam_auth_token.target_account data source
ibm_is_security_group.kube_vpeg_sg data source
ibm_is_subnet.cluster_subnet data source

Inputs

Name Description Type Default Required
access_tags Add existing access management tags to the Backup Recovery instance to manage access. list(string) [] no
add_cluster_tags Whether to add BRS tags to the cluster. Set to false if you manage cluster tags externally to avoid drift. When false, you should manually add the tags 'brs-region:' and 'brs-guid:' to your cluster. bool true no
add_dsc_rules_to_cluster_sg Set to true to automatically add the security group rules required by the Data Source Connector. This is mandatory when registering the cluster via its public service endpoint. Set to false to only register the cluster and create the policy without modifying security groups. bool false no
auto_protect_policy_name Name of an existing protection policy to use for auto-protect. Required when enable_auto_protect is true and deployment_mode is 'backup_only' or 'full_backup_recovery'. The policy must already exist in the BRS instance (create it using the terraform-ibm-backup-recovery module). string null no
backup_run_poll_interval_seconds Polling interval in seconds when waiting for the first restorable backup run. number 30 no
backup_run_poll_timeout_minutes Maximum time in minutes to poll for the first restorable backup run when recovery is enabled in a single apply. number 45 no
brs_connection_name Name of the connection from the Backup & Recovery Service instance to be used for protecting the cluster. If brs_create_new_connection is set to true (default), this will be the name of the new connection created. If set to false, this must be the name of an existing connection. string n/a yes
brs_create_new_connection Flag to create a new connection from the Backup & Recovery Service instance to the cluster. When set to true (default), a new connection is created with the name specified in brs_connection_name. When false, it uses an existing connection matching brs_connection_name. bool true no
brs_endpoint_type The endpoint type to use when connecting to the Backup and Recovery service for Terraform provider operations and script calls. Allowed values are 'public' or 'private'. When create_brs_vpe=true and this is set to 'private', the DSC pods reach BRS over the Virtual Private Endpoint Gateway (VPE) instead of the IBM Cloud Service Endpoint (CSE) — the BRS endpoint URL is automatically overridden to the VPE DNS hostname inside the cluster VPC. string "private" no
brs_instance_name Name of the Backup & Recovery Service instance. Required only when existing_brs_instance_crn is not provided. string null no
brs_resource_group_id The ID of the resource group where the BRS instance will be created. If this value is null, cluster_resource_group_id is used by default. This is suitable when the cluster and the BRS instance are in the same IBM Cloud account. Set this to a resource group in the target account when the BRS instance lives in a different IBM Cloud account from the cluster. string null no
brs_service_type The IBM Cloud service name for the Backup and Recovery instance. Use the default backup-recovery for production. Set to backup-recovery-tests to provision or connect against the test-environment service. string "backup-recovery" no
brs_vpe_name Override the name of the BRS Virtual Private Endpoint Gateway. If null, the name is auto-generated as '<brs_connection_name>-vpe'. string null no
cluster_config_endpoint_type The type of endpoint to use for the cluster config access: default, private, vpe, or link. The default value uses the default endpoint of the cluster. string "default" no
cluster_id The ID of the cluster designated for backup and recovery. string n/a yes
cluster_resource_group_id Resource group ID the cluster is deployed in. string n/a yes
connection_env_type Connection environment type to determine the required parameters for creating a new connection. Must be consistent with kube_type (use kIks* for kubernetes, kRoks* for openshift). Allowed values are 'kIksVpc', 'kRoksVpc', 'kRoksClassic', and 'kIksClassic'. string n/a yes
create_brs_vpe Set to true to create a Virtual Private Endpoint Gateway (VPEG) that routes traffic from the cluster VPC to the BRS instance over the IBM private backbone. For existing clusters, vpc_id and vpc_subnets are auto-discovered from the cluster's worker pools. When creating a new cluster in the same apply, supply vpc_id and vpc_subnets explicitly (the auto-discovery reads worker pools, which are unknown until after the cluster is applied). For cross-account setups (BRS in a different IBM Cloud account), the required S2S IAM authorization policy is created automatically when the module detects that the ibm.cluster provider belongs to a different account than the default ibm provider. bool false no
create_dsc_worker_pool Set to true to create a dedicated worker pool for the Data Source Connector in VPC clusters. If set to false, the connector will be deployed on existing worker nodes. bool true no
create_new_brs_instance Whether to provision a new Backup & Recovery Service instance. Leave as null (default) to infer the behaviour from existing_brs_instance_crn (a new instance is created when the CRN is not provided). Set to false to reuse an existing instance whose CRN is only known after apply — for example, when this module registers a second cluster against an instance created by a first invocation in the same apply. bool null no
deployment_mode Deployment mode to control what components are deployed:
- 'backup_only' (default): Registers source cluster with BRS, configures protection groups. No target cluster, no recovery.
- 'connected_component': Registers both source + target clusters with BRS for cluster connection setup only. No backup or recovery triggered.
- 'full_backup_recovery': End-to-end: registers clusters, triggers on-demand backup, waits for completion, executes recovery to validate.
string "backup_only" no
dsc_chart_uri The full OCI registry URI for the Data Source Connector Helm chart, including the digest. string "oci://icr.io/brs-charts/brs-ds-connector-chart:7.3.12-release-20260713-2e7241a2@sha256:b6a39948f5d1b6f765d0e73ebd2dd9c700ba0d5d5a120969efdfce2f75e8467e" no
dsc_helm_timeout Timeout in seconds for the Data Source Connector Helm deployment. number 7200 no
dsc_name Release name for the Data Source Connector Helm deployment. string "dsc" no
dsc_namespace The cluster namespace where the Data Source Connector will be installed. Will be created if it does not exist. string "ibm-brs-data-source-connector" no
dsc_pod_cpu_limits CPU limit for each Data Source Connector pod (e.g., '4', '8'). Should match or exceed the worker node's available CPU. string "4" no
dsc_pod_cpu_requests CPU request for each Data Source Connector pod (e.g., '2', '4'). This is the guaranteed CPU allocation. string "2" no
dsc_pod_memory_limits Memory limit for each Data Source Connector pod (e.g., '8Gi', '16Gi'). Should match or exceed the worker node's available memory. string "8Gi" no
dsc_pod_memory_requests Memory request for each Data Source Connector pod (e.g., '5Gi', '10Gi'). This is the guaranteed memory allocation. string "5Gi" no
dsc_replicas Number of Data Source Connector pods to run.
Recommended values:
• 3 – for high availability across multiple nodes/zones (strongly recommended in production)
• 1 – only for dev/test or single-node clusters
number 1 no
dsc_storage_class Storage class to use for the Data Source Connector persistent volume. By default, it uses 'ibmc-vpc-block-metro-5iops-tier' for VPC clusters and 'ibmc-block-silver' for Classic clusters. string null no
dsc_worker_pool_flavor The machine flavor for the Data Source Connector worker pool. bxf.4x16 (4 vCPU, 16 GB RAM) is available in every IBM Cloud VPC zone. Override for a larger flavor (e.g. bxf.8x32). string "bxf.4x16" no
dsc_worker_pool_zones Number of zones to create worker pools in. Defaults to 1 for single-zone deployments. Set to 2 or 3 for multi-zone high availability. Must be between 1 and 3. number 1 no
enable_auto_protect Enable auto-protect during the initial cluster registration. This must be set to true on the first run; toggling it from false to true later is not supported by the underlying API and will not retroactively create the protection group. bool true no
existing_brs_instance_crn CRN of the Backup & Recovery Service instance. Supports both production (backup-recovery) and test (backup-recovery-tests) service instances. string null no
ibmcloud_api_key The IBM Cloud api key to generate an IAM token. string n/a yes
install_required_binaries When set to true, a script will run to check if kubectl and jq, IBM Cloud CLI (ibmcloud), and the backup-recovery plugin exist on the runtime and if not attempt to download them from the public internet and install them to /tmp. Set to false to skip running this script. bool true no
kube_type Specify the type of target cluster for the backup and recovery. Accepted values are openshift or kubernetes. string "openshift" no
policies A list of protection policies to create or look up. Set create_new_policy to true (default) to create a new policy with the specified schedule and retention. Set create_new_policy to false to reference an existing policy by name.
list(object({
name = string
create_new_policy = optional(bool, false)
use_default_backup_target = optional(bool, true)

# --- primary_backup_target advanced details ---
primary_backup_target_details = optional(object({
target_id = number
tier_settings = optional(list(object({
cloud_platform = string # AWS, Azure, Google, Oracle
aws_tiering = optional(object({
tiers = list(object({ tier_type = string, move_after = number, move_after_unit = string }))
}))
azure_tiering = optional(object({
tiers = list(object({ tier_type = string, move_after = number, move_after_unit = string }))
}))
google_tiering = optional(object({
tiers = list(object({ tier_type = string, move_after = number, move_after_unit = string }))
}))
oracle_tiering = optional(object({
tiers = list(object({ tier_type = string, move_after = number, move_after_unit = string }))
}))
})))
}))

# --- Standard backup schedule and retention ---
schedule = optional(object({
unit = string
minute_schedule = optional(object({ frequency = number }))
hour_schedule = optional(object({ frequency = number }))
day_schedule = optional(object({ frequency = number }))
week_schedule = optional(object({ day_of_week = list(string) }))
month_schedule = optional(object({ day_of_month = optional(number), day_of_week = optional(list(string)), week_of_month = optional(string) }))
year_schedule = optional(object({ day_of_year = string }))
}))
retention = optional(object({
duration = number
unit = string
data_lock_config = optional(object({ mode = string, unit = string, duration = number, enable_worm_on_external_target = optional(bool, false) }))
}))

# --- Bare Metal Recovery (BMR) ---
bmr = optional(object({
schedule = optional(object({
unit = string
minute_schedule = optional(object({ frequency = number }))
hour_schedule = optional(object({ frequency = number }))
day_schedule = optional(object({ frequency = number }))
week_schedule = optional(object({ day_of_week = list(string) }))
month_schedule = optional(object({ day_of_month = optional(number), day_of_week = optional(list(string)), week_of_month = optional(string) }))
year_schedule = optional(object({ day_of_year = string }))
}))
retention = object({
duration = number
unit = string
data_lock_config = optional(object({ mode = string, unit = string, duration = number, enable_worm_on_external_target = optional(bool, false) }))
})
}))

# --- Continuous Data Protection (CDP) ---
cdp = optional(object({
retention = object({
duration = number
unit = string
data_lock_config = optional(object({ mode = string, unit = string, duration = number, enable_worm_on_external_target = optional(bool, false) }))
})
}))

# --- Database Log Backup ---
log = optional(object({
schedule = object({
unit = string
minute_schedule = optional(object({ frequency = number }))
hour_schedule = optional(object({ frequency = number }))
day_schedule = optional(object({ frequency = number }))
week_schedule = optional(object({ day_of_week = list(string) }))
month_schedule = optional(object({ day_of_month = optional(number), day_of_week = optional(list(string)), week_of_month = optional(string) }))
year_schedule = optional(object({ day_of_year = string }))
})
retention = object({
duration = number
unit = string
data_lock_config = optional(object({ mode = string, unit = string, duration = number, enable_worm_on_external_target = optional(bool, false) }))
})
}))

# --- Storage Array Snapshot ---
storage_array_snapshot = optional(object({
schedule = object({
unit = string
minute_schedule = optional(object({ frequency = number }))
hour_schedule = optional(object({ frequency = number }))
day_schedule = optional(object({ frequency = number }))
week_schedule = optional(object({ day_of_week = list(string) }))
month_schedule = optional(object({ day_of_month = optional(number), day_of_week = optional(list(string)), week_of_month = optional(string) }))
year_schedule = optional(object({ day_of_year = string }))
})
retention = object({
duration = number
unit = string
data_lock_config = optional(object({ mode = string, unit = string, duration = number, enable_worm_on_external_target = optional(bool, false) }))
})
}))

# --- Blackout windows ---
blackout_window = optional(list(object({
day = string
start_time = object({
hour = number
minute = number
time_zone = optional(string, "America/New_York")
})
end_time = object({
hour = number
minute = number
time_zone = optional(string, "America/New_York")
})
})))

# --- Run timeouts (prevent hung backup jobs) ---
run_timeouts = optional(list(object({
timeout_mins = number
backup_type = optional(string, "kRegular")
})))

# --- Extended retention (keep certain snapshots longer) ---
extended_retention = optional(list(object({
schedule = object({
unit = string
frequency = number
})
retention = object({
duration = number
unit = string
data_lock_config = optional(object({
mode = string
unit = string
duration = number
enable_worm_on_external_target = optional(bool, false)
}))
})
run_type = optional(string, "Regular")
config_id = optional(string)
})))

# --- Cascaded Targets Config ---
cascaded_targets_config = optional(object({
source_cluster_id = number
remote_targets = list(object({
archival_targets = optional(list(object({
target_id = number
backup_run_type = optional(string)
config_id = optional(string)
copy_on_run_success = optional(bool)
schedule = object({
unit = string
frequency = optional(number)
})
retention = object({
duration = number
unit = string
data_lock_config = optional(object({ mode = string, unit = string, duration = number, enable_worm_on_external_target = optional(bool, false) }))
})
extended_retention = optional(list(object({
schedule = object({
unit = string
frequency = number
})
retention = object({
duration = number
unit = string
data_lock_config = optional(object({ mode = string, unit = string, duration = number, enable_worm_on_external_target = optional(bool, false) }))
})
run_type = optional(string, "Regular")
config_id = optional(string)
})))
})))
cloud_spin_targets = optional(list(object({
target = object({
id = optional(number)
})
backup_run_type = optional(string)
config_id = optional(string)
copy_on_run_success = optional(bool)
schedule = object({
unit = string
frequency = optional(number)
})
retention = object({
duration = number
unit = string
data_lock_config = optional(object({ mode = string, unit = string, duration = number, enable_worm_on_external_target = optional(bool, false) }))
})
extended_retention = optional(list(object({
schedule = object({
unit = string
frequency = number
})
retention = object({
duration = number
unit = string
data_lock_config = optional(object({ mode = string, unit = string, duration = number, enable_worm_on_external_target = optional(bool, false) }))
})
run_type = optional(string, "Regular")
config_id = optional(string)
})))
})))
replication_targets = optional(list(object({
target_type = string
target_id = number
backup_run_type = optional(string)
config_id = optional(string)
copy_on_run_success = optional(bool)
schedule = object({
unit = string
frequency = optional(number)
})
retention = object({
duration = number
unit = string
data_lock_config = optional(object({ mode = string, unit = string, duration = number, enable_worm_on_external_target = optional(bool, false) }))
})
extended_retention = optional(list(object({
schedule = object({
unit = string
frequency = number
})
retention = object({
duration = number
unit = string
data_lock_config = optional(object({ mode = string, unit = string, duration = number, enable_worm_on_external_target = optional(bool, false) }))
})
run_type = optional(string, "Regular")
config_id = optional(string)
})))
})))
}))
}))
}))
null no
protection_groups List of protection groups for granular backup control. Each group selects specific namespaces/objects and applies a policy. Use this as an alternative to enable_auto_protect for fine-grained control over which workloads are backed up.
list(object({
name = string
policy_name = string
description = optional(string)

# --- Kubernetes-specific params ---
enable_indexing = optional(bool, true)
leverage_csi_snapshot = optional(bool, false)
non_snapshot_backup = optional(bool, false)
volume_backup_failure = optional(bool, false)

# Objects (namespaces) to protect
objects = optional(list(object({
id = optional(number)
name = optional(string)
backup_only_pvc = optional(bool, false)
fail_backup_on_hook_failure = optional(bool, false)
included_resources = optional(list(string))
excluded_resources = optional(list(string))
include_pvcs = optional(list(object({
id = optional(number)
name = optional(string)
})))
exclude_pvcs = optional(list(object({
id = optional(number)
name = optional(string)
})))

# Per-object label-based PV/PVC inclusion
include_params = optional(object({
label_combination_method = optional(string, "AND") # AND, OR
label_vector = optional(list(object({
key = string
value = string
})))
objects = optional(list(object({}))) # usually any or map but provider says array of objects
selected_resources = optional(list(object({
api_group = optional(string)
is_cluster_scoped = optional(bool)
kind = optional(string)
name = optional(string)
version = optional(string)
resource_list = optional(list(object({
entity_id = optional(number)
name = optional(string)
})))
})))
}))

# Per-object label-based PV/PVC exclusion
exclude_params = optional(object({
label_combination_method = optional(string, "AND")
label_vector = optional(list(object({
key = string
value = string
})))
objects = optional(list(object({})))
selected_resources = optional(list(object({
api_group = optional(string)
is_cluster_scoped = optional(bool)
kind = optional(string)
name = optional(string)
version = optional(string)
resource_list = optional(list(object({
entity_id = optional(number)
name = optional(string)
})))
})))
}))

# Quiescing rules for app-consistent backups
quiesce_groups = optional(list(object({
quiesce_mode = string # kQuiesceTogether, kQuiesceIndependently
quiesce_rules = list(object({
pod_selector_labels = optional(list(object({
key = string
value = string
})))
pre_snapshot_hooks = list(object({
commands = list(string)
container = optional(string)
fail_on_error = optional(bool, false)
timeout = optional(number)
}))
post_snapshot_hooks = list(object({
commands = list(string)
container = optional(string)
fail_on_error = optional(bool, false)
timeout = optional(number)
}))
}))
})))
})))

# Object IDs to exclude
exclude_object_ids = optional(list(number))

# Label-based namespace selection (2D array of label IDs)
label_ids = optional(list(number))
exclude_label_ids = optional(list(number))

# Global label-based inclusion filter
include_params = optional(object({
label_combination_method = optional(string, "AND") # AND, OR
label_vector = optional(list(object({
key = string
value = string
})))
objects = optional(list(object({})))
selected_resources = optional(list(object({
api_group = optional(string)
is_cluster_scoped = optional(bool)
kind = optional(string)
name = optional(string)
version = optional(string)
resource_list = optional(list(object({
entity_id = optional(number)
name = optional(string)
})))
})))
}))

# Global label-based exclusion filter
exclude_params = optional(object({
label_combination_method = optional(string, "AND")
label_vector = optional(list(object({
key = string
value = string
})))
objects = optional(list(object({})))
selected_resources = optional(list(object({
api_group = optional(string)
is_cluster_scoped = optional(bool)
kind = optional(string)
name = optional(string)
version = optional(string)
resource_list = optional(list(object({
entity_id = optional(number)
name = optional(string)
})))
})))
}))

# --- Alert policy ---
alert_policy = optional(object({
backup_run_status = list(string) # kFailure, kSuccess, kSlaViolation, kWarning
alert_targets = optional(list(object({
email_address = string
language = optional(string, "en-us")
recipient_type = optional(string, "kTo")
})))
raise_object_level_failure_alert = optional(bool)
raise_object_level_failure_alert_after_each_attempt = optional(bool)
raise_object_level_failure_alert_after_last_attempt = optional(bool)
}))

# --- SLA ---
sla = optional(list(object({
backup_run_type = optional(string, "kIncremental") # kIncremental, kFull, kLog
sla_minutes = number
})))

# --- Scheduling ---
start_time = optional(object({
hour = number
minute = number
time_zone = optional(string, "America/Los_Angeles")
}))

# --- Advanced configs (key/value pairs) ---
advanced_configs = optional(list(object({
key = string
value = string
})))

priority = optional(string, "kMedium") # kLow, kMedium, kHigh
qos_policy = optional(string) # kBackupHDD, kBackupSSD, etc.
is_paused = optional(bool, false)
abort_in_blackouts = optional(bool, false)
pause_in_blackouts = optional(bool, false)
}))
[] no
recoveries List of recovery operations to restore backups. These operations are triggered automatically after a backup run completes when recovery is enabled by the calling module. Each entry's kubernetes_params.objects[*].snapshot_id controls which backup is restored: supply an explicit snapshot ID to recover from any specific backup (not necessarily the one taken in the current apply), or use the latest_snapshots output to reference the most recent run. Supports multiple environments: Kubernetes, VMware, Physical, AWS, Azure, GCP, SQL, Oracle, and more. This variable follows the official IBM Backup Recovery provider schema. For IKS/ROKS recovery use kubernetes_params. See the Usage section in the README for examples.
list(object({
name = string
snapshot_environment = string # kKubernetes, kVMware, kPhysical, kAWS, kAzure, kGCP, kSQL, kOracle, kView, etc.

# Kubernetes-specific recovery parameters
kubernetes_params = optional(object({
recovery_action = string # RecoverNamespaces, RecoverPVs, RecoverApps

objects = list(object({
snapshot_id = string
point_in_time_usecs = optional(number)
protection_group_id = optional(string)
protection_group_name = optional(string)
recover_from_standby = optional(bool, false)
}))
}))

# VMware-specific recovery parameters (for future provider support)
vmware_params = optional(object({
recovery_action = optional(string)
objects = optional(list(object({
snapshot_id = optional(string)
point_in_time_usecs = optional(number)
protection_group_id = optional(string)
protection_group_name = optional(string)
recover_from_standby = optional(bool, false)
})))
}))

# Physical server recovery parameters (for future provider support)
physical_params = optional(object({
recovery_action = optional(string)
objects = optional(list(object({
snapshot_id = optional(string)
point_in_time_usecs = optional(number)
protection_group_id = optional(string)
protection_group_name = optional(string)
})))
}))

# AWS-specific recovery parameters (for future provider support)
aws_params = optional(object({
recovery_action = optional(string)
objects = optional(list(object({
snapshot_id = optional(string)
point_in_time_usecs = optional(number)
protection_group_id = optional(string)
protection_group_name = optional(string)
})))
}))

# Azure-specific recovery parameters (for future provider support)
azure_params = optional(object({
recovery_action = optional(string)
objects = optional(list(object({
snapshot_id = optional(string)
point_in_time_usecs = optional(number)
protection_group_id = optional(string)
protection_group_name = optional(string)
})))
}))

# GCP-specific recovery parameters (for future provider support)
gcp_params = optional(object({
recovery_action = optional(string)
objects = optional(list(object({
snapshot_id = optional(string)
point_in_time_usecs = optional(number)
protection_group_id = optional(string)
protection_group_name = optional(string)
})))
}))

# SQL-specific recovery parameters (for future provider support)
sql_params = optional(object({
recovery_action = optional(string)
objects = optional(list(object({
snapshot_id = optional(string)
point_in_time_usecs = optional(number)
protection_group_id = optional(string)
protection_group_name = optional(string)
})))
}))

# Oracle-specific recovery parameters (for future provider support)
oracle_params = optional(object({
recovery_action = optional(string)
objects = optional(list(object({
snapshot_id = optional(string)
point_in_time_usecs = optional(number)
protection_group_id = optional(string)
protection_group_name = optional(string)
})))
}))
}))
[] no
recovery_mode Recovery mode: 'same-cluster' to restore within the same cluster, or 'cross-cluster' to restore to a different target cluster. This is used when recovery is enabled by the calling module. string "same-cluster" no
region Region where the Backup & Recovery Service instance needs to be created. string null no
registration_images The images required for backup and recovery registration.
object({
data_mover = string
velero = string
velero_aws_plugin = string
velero_openshift_plugin = optional(string, null)
cohesity_dataprotect_plugin = string
init_container = optional(string, null)
})
{
"cohesity_dataprotect_plugin": "icr.io/ext/brs/cohesity-dataprotect-plugin:7.3.12@sha256:7c68cf893694f1057c700cfe223fdd86ae455889295f9f353c22d9d443170182",
"data_mover": "icr.io/ext/brs/cohesity-datamover:7.3.12@sha256:0a0e87ddce1165398390656e6ff9465815a105a79c427030f284bffbc2b7d987",
"velero": "icr.io/ext/brs/oadp-velero:1.5.5@sha256:386ef8dff743339a40b3b82a1fcdc9bda56c0d31a0b544acae59058d522db8d7",
"velero_aws_plugin": "icr.io/ext/brs/oadp-velero-plugin-for-aws:1.5.5@sha256:93a7f4c514546a1d771186b8e60722d3b1632190ebc02f4a8e75e8fe0f867ced",
"velero_openshift_plugin": "icr.io/ext/brs/oadp-velero-plugin-for-openshift:1.5.5@sha256:130894d2eca06a0e5eb49b969c03c99ed0d5a9d0cac83126e360442d438c9f2d"
}
no
resource_tags Add user resource tags to the Backup Recovery instance to organize, track, and manage costs. list(string) [] no
target_cluster_id Target cluster ID for cross-cluster recovery or connected component setup. Required when var.recovery_mode is 'cross-cluster' or when deployment_mode is 'connected_component'. Must be a cluster already registered with the BRS instance. string null no
target_cluster_resource_group_id Resource group ID of the target cluster for cross-cluster recovery or connected component setup. Required when recovery_mode is 'cross-cluster' or when deployment_mode is 'connected_component'. string null no
vpc_id ID of the VPC where the BRS Virtual Private Endpoint Gateway will be created. Optional when create_brs_vpe is true — when omitted the VPC ID is auto-discovered from the cluster's worker-pool subnets. Supply this explicitly only when the auto-discovery would pick the wrong VPC. string null no
vpc_subnets List of subnets in which to bind reserved IPs for the BRS VPE Gateway. Each entry must have 'name', 'id', and 'zone'. Optional when create_brs_vpe is true — when omitted all subnets in the cluster VPC are discovered automatically. Supply this explicitly to restrict the VPEG to a specific subset of subnets.
list(object({
name = string
id = string
zone = string
}))
[] no
wait_till To avoid long wait times when you run your Terraform code, you can specify the stage when you want Terraform to mark the cluster resource creation as completed. Depending on what stage you choose, the cluster creation might not be fully completed and continues to run in the background. However, your Terraform code can continue to run without waiting for the cluster to be fully created. Supported args are MasterNodeReady, OneWorkerNodeReady, IngressReady and Normal string "Normal" no
wait_till_timeout Timeout for wait_till in minutes. number 90 no

Outputs

Name Description
auto_protect_pg_id ID of the auto-protect protection group created by BRS when enable_auto_protect=true. Null when auto-protect is not enabled or the registration has not yet propagated.
backup_runs_summary Summary of backup runs per protection group. Shows run count and latest run status. Empty if recovery is not enabled by the calling module.
brs_instance_crn CRN of the Backup & Recovery Service instance
brs_instance_guid GUID of the Backup & Recovery Service instance
brs_instance_url Endpoint URL for the BRS instance, derived from the IBM Cloud resource extensions. Correct for both staging and production environments.
brs_tags BRS tags that should be added to the cluster to prevent tag drift. Include these in your cluster's tags input.
brs_tenant_id Tenant ID of the Backup & Recovery Service instance
brs_vpe_ips Map of VPEG name to reserved IP list. Populated only when create_brs_vpe = true; empty map otherwise. Each entry contains the private IPs bound to each subnet zone.
connection_id ID of the data source connection to the Backup & Recovery Service instance
latest_snapshots Map of protection group names to the most recent successful snapshot ID per protection group. Populated only when recovery is enabled by the calling module, because snapshot discovery relies on the backup-polling infrastructure (terraform_data.wait_for_backup_run and data.ibm_backup_recovery_protection_group_runs) that is activated when recovery is enabled. Use the snapshot IDs from this output as explicit snapshot_id values in a recovery's kubernetes_params.objects to target a specific backup rather than always recovering the latest.
protection_group_ids Map of protection group names to their IDs. Empty if protection groups are not deployed.
protection_sources List of protection sources.
recovery_ids Map of recovery operation names to their IDs. Empty if recovery is not enabled.
recovery_status Map of recovery operation names to their status information. Empty if recovery is not enabled by the calling module.
s2s_auth_policies S2S IAM authorization policies created in this account. Populated only for cross-account VPE deployments (when ibm.cluster and ibm provider resolve to different accounts); empty map otherwise.
source_registration_id ID of the registered Kubernetes source.
target_cluster_id Target cluster ID for recovery operations. Same as source cluster for same-cluster recovery mode.

Contributing

You can report issues and request features for this module in GitHub issues in the module repo. See Report an issue or request a feature.

To set up your local development environment, see Local development setup in the project documentation.

Requirements

Name Version
terraform >= 1.9.0
helm >=3.1.0, <4.0.0
ibm >= 1.88.3, < 3.0.0
kubernetes >=3.0.1, <4.0.0
time >=0.12.1, <1.0.0

Providers

Name Version
helm 3.1.1
ibm 2.1.0
kubernetes 3.1.0
terraform n/a
time 0.14.0

Modules

Name Source Version
backup_recovery_instance terraform-ibm-modules/backup-recovery/ibm v1.10.2
crn_parser terraform-ibm-modules/common-utilities/ibm//modules/crn-parser 1.5.0
dsc_sg_rule terraform-ibm-modules/security-group/ibm v2.9.0

Resources

Name Type
helm_release.data_source_connector resource
ibm_backup_recovery.recover_snapshot resource
ibm_backup_recovery_protection_group.protection_group resource
ibm_backup_recovery_source_registration.source_registration resource
ibm_container_vpc_worker_pool.data_source_connector resource
ibm_resource_tag.cluster_brs_tag resource
kubernetes_cluster_role_binding_v1.brsagent_admin resource
kubernetes_namespace_v1.dsc_namespace resource
kubernetes_secret_v1.brsagent_token resource
kubernetes_service_account_v1.brsagent resource
terraform_data.cleanup_brs_agent_resources resource
terraform_data.delete_auto_protect_pg resource
terraform_data.install_dependencies resource
terraform_data.wait_before_helm_destroy resource
terraform_data.wait_for_backup_run resource
time_sleep.wait_for_backup_completion resource
time_sleep.wait_for_source_discovery resource
ibm_backup_recovery_protection_group_runs.backup_runs data source
ibm_backup_recovery_protection_sources.sources data source
ibm_container_cluster.classic_cluster data source
ibm_container_cluster_config.cluster_config data source
ibm_container_vpc_cluster.vpc_cluster data source
ibm_container_vpc_worker_pool.pool data source

Inputs

Name Description Type Default Required
access_tags Add existing access management tags to the Backup Recovery instance to manage access. list(string) [] no
add_cluster_tags Whether to add BRS tags to the cluster. Set to false if you manage cluster tags externally to avoid drift. When false, you should manually add the tags 'brs-region:' and 'brs-guid:' to your cluster. bool true no
add_dsc_rules_to_cluster_sg Set to true to automatically add the security group rules required by the Data Source Connector. This is mandatory when registering the cluster via its public service endpoint. Set to false to only register the cluster and create the policy without modifying security groups. bool true no
auto_protect_policy_name Name of an existing protection policy to use for auto-protect. Required when enable_auto_protect is true. The policy must already exist in the BRS instance (create it using the terraform-ibm-backup-recovery module). string null no
backup_run_poll_interval_seconds Polling interval in seconds when waiting for the first restorable backup run. number 30 no
backup_run_poll_timeout_minutes Maximum time in minutes to poll for the first restorable backup run when recovery is enabled in a single apply. number 45 no
brs_connection_name Name of the connection from the Backup & Recovery Service instance to be used for protecting the cluster. If brs_create_new_connection is set to true (default), this will be the name of the new connection created. If set to false, this must be the name of an existing connection. string n/a yes
brs_create_new_connection Flag to create a new connection from the Backup & Recovery Service instance to the cluster. When set to true (default), a new connection is created with the name specified in brs_connection_name. When false, it uses an existing connection matching brs_connection_name. bool true no
brs_endpoint_type The endpoint type to use when connecting to the Backup and Recovery service for creating a data source connection. Allowed values are 'public' or 'private'. string "private" no
brs_instance_name Name of the Backup & Recovery Service instance. Required only when existing_brs_instance_crn is not provided. string null no
cluster_config_endpoint_type The type of endpoint to use for the cluster config access: default, private, vpe, or link. The default value uses the default endpoint of the cluster. string "default" no
cluster_id The ID of the cluster designated for backup and recovery. string n/a yes
cluster_resource_group_id Resource group ID the cluster is deployed in. string n/a yes
connection_env_type Connection environment type to determine the required parameters for creating a new connection. Allowed values are 'kIksVpc', 'kRoksVpc', 'kRoksClassic', and 'kIksClassic'. string "kIksVpc" no
create_dsc_worker_pool Set to true to create a dedicated worker pool for the Data Source Connector in VPC clusters. If set to false, the connector will be deployed on existing worker nodes. bool true no
create_new_brs_instance Whether to provision a new Backup & Recovery Service instance. Leave as null (default) to infer the behaviour from existing_brs_instance_crn (a new instance is created when the CRN is not provided). Set to false to reuse an existing instance whose CRN is only known after apply — for example, when this module registers a second cluster against an instance created by a first invocation in the same apply. bool null no
dsc_chart_uri The full OCI registry URI for the Data Source Connector Helm chart, including the digest. string "oci://icr.io/ext/brs/brs-ds-connector-chart:7.2.18-release-20260226-49768040@sha256:99728a3146a7d8b2ae2f88300a6a89752488d3733e29118ee83a655959114541" no
dsc_helm_timeout Timeout in seconds for the Data Source Connector Helm deployment. number 3600 no
dsc_image_version Container image for the Data Source Connector. string "icr.io/ext/brs/brs-ds-connector:7.2.18-release-20260226-49768040@sha256:99728a3146a7d8b2ae2f88300a6a89752488d3733e29118ee83a655959114541" no
dsc_name Release name for the Data Source Connector Helm deployment. string "dsc" no
dsc_namespace The cluster namespace where the Data Source Connector will be installed. Will be created if it does not exist. string "ibm-brs-data-source-connector" no
dsc_replicas Number of Data Source Connector pods to run.
Recommended values:
• 3 – for high availability across multiple nodes/zones (strongly recommended in production)
• 1 – only for dev/test or single-node clusters
number 1 no
dsc_storage_class Storage class to use for the Data Source Connector persistent volume. By default, it uses 'ibmc-vpc-block-metro-5iops-tier' for VPC clusters and 'ibmc-block-silver' for Classic clusters. string null no
enable_auto_protect Enable auto-protect during the initial cluster registration. This must be set to true on the first run; toggling it from false to true later is not supported by the underlying API and will not retroactively create the protection group. bool true no
enable_recovery Enable automatic recovery after backup completion. When true, recovery operations defined in recoveries will be triggered automatically after successful backup. Set to false to only perform backups without recovery. bool false no
existing_brs_instance_crn CRN of the Backup & Recovery Service instance. string null no
ibmcloud_api_key The IBM Cloud api key to generate an IAM token. string n/a yes
install_required_binaries When set to true, a script will run to check if kubectl and jq, IBM Cloud CLI (ibmcloud), and the backup-recovery plugin exist on the runtime and if not attempt to download them from the public internet and install them to /tmp. Set to false to skip running this script. bool true no
kube_type Specify the type of target cluster for the backup and recovery. Accepted values are openshift or kubernetes. string "openshift" no
policies A list of protection policies to create or look up. Set create_new_policy to true (default) to create a new policy with the specified schedule and retention. Set create_new_policy to false to reference an existing policy by name.
list(object({
name = string
create_new_policy = optional(bool, false)
use_default_backup_target = optional(bool, true)

# --- primary_backup_target advanced details ---
primary_backup_target_details = optional(object({
target_id = number
tier_settings = optional(list(object({
cloud_platform = string # AWS, Azure, Google, Oracle
aws_tiering = optional(object({
tiers = list(object({ tier_type = string, move_after = number, move_after_unit = string }))
}))
azure_tiering = optional(object({
tiers = list(object({ tier_type = string, move_after = number, move_after_unit = string }))
}))
google_tiering = optional(object({
tiers = list(object({ tier_type = string, move_after = number, move_after_unit = string }))
}))
oracle_tiering = optional(object({
tiers = list(object({ tier_type = string, move_after = number, move_after_unit = string }))
}))
})))
}))

# --- Standard backup schedule and retention ---
schedule = optional(object({
unit = string
minute_schedule = optional(object({ frequency = number }))
hour_schedule = optional(object({ frequency = number }))
day_schedule = optional(object({ frequency = number }))
week_schedule = optional(object({ day_of_week = list(string) }))
month_schedule = optional(object({ day_of_month = optional(number), day_of_week = optional(list(string)), week_of_month = optional(string) }))
year_schedule = optional(object({ day_of_year = string }))
}))
retention = optional(object({
duration = number
unit = string
data_lock_config = optional(object({ mode = string, unit = string, duration = number, enable_worm_on_external_target = optional(bool, false) }))
}))

# --- Bare Metal Recovery (BMR) ---
bmr = optional(object({
schedule = optional(object({
unit = string
minute_schedule = optional(object({ frequency = number }))
hour_schedule = optional(object({ frequency = number }))
day_schedule = optional(object({ frequency = number }))
week_schedule = optional(object({ day_of_week = list(string) }))
month_schedule = optional(object({ day_of_month = optional(number), day_of_week = optional(list(string)), week_of_month = optional(string) }))
year_schedule = optional(object({ day_of_year = string }))
}))
retention = object({
duration = number
unit = string
data_lock_config = optional(object({ mode = string, unit = string, duration = number, enable_worm_on_external_target = optional(bool, false) }))
})
}))

# --- Continuous Data Protection (CDP) ---
cdp = optional(object({
retention = object({
duration = number
unit = string
data_lock_config = optional(object({ mode = string, unit = string, duration = number, enable_worm_on_external_target = optional(bool, false) }))
})
}))

# --- Database Log Backup ---
log = optional(object({
schedule = object({
unit = string
minute_schedule = optional(object({ frequency = number }))
hour_schedule = optional(object({ frequency = number }))
day_schedule = optional(object({ frequency = number }))
week_schedule = optional(object({ day_of_week = list(string) }))
month_schedule = optional(object({ day_of_month = optional(number), day_of_week = optional(list(string)), week_of_month = optional(string) }))
year_schedule = optional(object({ day_of_year = string }))
})
retention = object({
duration = number
unit = string
data_lock_config = optional(object({ mode = string, unit = string, duration = number, enable_worm_on_external_target = optional(bool, false) }))
})
}))

# --- Storage Array Snapshot ---
storage_array_snapshot = optional(object({
schedule = object({
unit = string
minute_schedule = optional(object({ frequency = number }))
hour_schedule = optional(object({ frequency = number }))
day_schedule = optional(object({ frequency = number }))
week_schedule = optional(object({ day_of_week = list(string) }))
month_schedule = optional(object({ day_of_month = optional(number), day_of_week = optional(list(string)), week_of_month = optional(string) }))
year_schedule = optional(object({ day_of_year = string }))
})
retention = object({
duration = number
unit = string
data_lock_config = optional(object({ mode = string, unit = string, duration = number, enable_worm_on_external_target = optional(bool, false) }))
})
}))

# --- Blackout windows ---
blackout_window = optional(list(object({
day = string
start_time = object({
hour = number
minute = number
time_zone = optional(string, "America/New_York")
})
end_time = object({
hour = number
minute = number
time_zone = optional(string, "America/New_York")
})
})))

# --- Run timeouts (prevent hung backup jobs) ---
run_timeouts = optional(list(object({
timeout_mins = number
backup_type = optional(string, "kRegular")
})))

# --- Extended retention (keep certain snapshots longer) ---
extended_retention = optional(list(object({
schedule = object({
unit = string
frequency = number
})
retention = object({
duration = number
unit = string
data_lock_config = optional(object({
mode = string
unit = string
duration = number
enable_worm_on_external_target = optional(bool, false)
}))
})
run_type = optional(string, "Regular")
config_id = optional(string)
})))

# --- Cascaded Targets Config ---
cascaded_targets_config = optional(object({
source_cluster_id = number
remote_targets = list(object({
archival_targets = optional(list(object({
target_id = number
backup_run_type = optional(string)
config_id = optional(string)
copy_on_run_success = optional(bool)
schedule = object({
unit = string
frequency = optional(number)
})
retention = object({
duration = number
unit = string
data_lock_config = optional(object({ mode = string, unit = string, duration = number, enable_worm_on_external_target = optional(bool, false) }))
})
extended_retention = optional(list(object({
schedule = object({
unit = string
frequency = number
})
retention = object({
duration = number
unit = string
data_lock_config = optional(object({ mode = string, unit = string, duration = number, enable_worm_on_external_target = optional(bool, false) }))
})
run_type = optional(string, "Regular")
config_id = optional(string)
})))
})))
cloud_spin_targets = optional(list(object({
target = object({
id = optional(number)
})
backup_run_type = optional(string)
config_id = optional(string)
copy_on_run_success = optional(bool)
schedule = object({
unit = string
frequency = optional(number)
})
retention = object({
duration = number
unit = string
data_lock_config = optional(object({ mode = string, unit = string, duration = number, enable_worm_on_external_target = optional(bool, false) }))
})
extended_retention = optional(list(object({
schedule = object({
unit = string
frequency = number
})
retention = object({
duration = number
unit = string
data_lock_config = optional(object({ mode = string, unit = string, duration = number, enable_worm_on_external_target = optional(bool, false) }))
})
run_type = optional(string, "Regular")
config_id = optional(string)
})))
})))
replication_targets = optional(list(object({
target_type = string
target_id = number
backup_run_type = optional(string)
config_id = optional(string)
copy_on_run_success = optional(bool)
schedule = object({
unit = string
frequency = optional(number)
})
retention = object({
duration = number
unit = string
data_lock_config = optional(object({ mode = string, unit = string, duration = number, enable_worm_on_external_target = optional(bool, false) }))
})
extended_retention = optional(list(object({
schedule = object({
unit = string
frequency = number
})
retention = object({
duration = number
unit = string
data_lock_config = optional(object({ mode = string, unit = string, duration = number, enable_worm_on_external_target = optional(bool, false) }))
})
run_type = optional(string, "Regular")
config_id = optional(string)
})))
})))
}))
}))
}))
null no
protection_groups List of protection groups for granular backup control. Each group selects specific namespaces/objects and applies a policy. Use this as an alternative to enable_auto_protect for fine-grained control over which workloads are backed up.
list(object({
name = string
policy_name = string
description = optional(string)

# --- Kubernetes-specific params ---
enable_indexing = optional(bool, true)
leverage_csi_snapshot = optional(bool, false)
non_snapshot_backup = optional(bool, false)
volume_backup_failure = optional(bool, false)

# Objects (namespaces) to protect
objects = optional(list(object({
id = optional(number)
name = optional(string)
backup_only_pvc = optional(bool, false)
fail_backup_on_hook_failure = optional(bool, false)
included_resources = optional(list(string))
excluded_resources = optional(list(string))
include_pvcs = optional(list(object({
id = optional(number)
name = optional(string)
})))
exclude_pvcs = optional(list(object({
id = optional(number)
name = optional(string)
})))

# Per-object label-based PV/PVC inclusion
include_params = optional(object({
label_combination_method = optional(string, "AND") # AND, OR
label_vector = optional(list(object({
key = string
value = string
})))
objects = optional(list(object({}))) # usually any or map but provider says array of objects
selected_resources = optional(list(object({
api_group = optional(string)
is_cluster_scoped = optional(bool)
kind = optional(string)
name = optional(string)
version = optional(string)
resource_list = optional(list(object({
entity_id = optional(number)
name = optional(string)
})))
})))
}))

# Per-object label-based PV/PVC exclusion
exclude_params = optional(object({
label_combination_method = optional(string, "AND")
label_vector = optional(list(object({
key = string
value = string
})))
objects = optional(list(object({})))
selected_resources = optional(list(object({
api_group = optional(string)
is_cluster_scoped = optional(bool)
kind = optional(string)
name = optional(string)
version = optional(string)
resource_list = optional(list(object({
entity_id = optional(number)
name = optional(string)
})))
})))
}))

# Quiescing rules for app-consistent backups
quiesce_groups = optional(list(object({
quiesce_mode = string # kQuiesceTogether, kQuiesceIndependently
quiesce_rules = list(object({
pod_selector_labels = optional(list(object({
key = string
value = string
})))
pre_snapshot_hooks = list(object({
commands = list(string)
container = optional(string)
fail_on_error = optional(bool, false)
timeout = optional(number)
}))
post_snapshot_hooks = list(object({
commands = list(string)
container = optional(string)
fail_on_error = optional(bool, false)
timeout = optional(number)
}))
}))
})))
})))

# Object IDs to exclude
exclude_object_ids = optional(list(number))

# Label-based namespace selection (2D array of label IDs)
label_ids = optional(list(number))
exclude_label_ids = optional(list(number))

# Global label-based inclusion filter
include_params = optional(object({
label_combination_method = optional(string, "AND") # AND, OR
label_vector = optional(list(object({
key = string
value = string
})))
objects = optional(list(object({})))
selected_resources = optional(list(object({
api_group = optional(string)
is_cluster_scoped = optional(bool)
kind = optional(string)
name = optional(string)
version = optional(string)
resource_list = optional(list(object({
entity_id = optional(number)
name = optional(string)
})))
})))
}))

# Global label-based exclusion filter
exclude_params = optional(object({
label_combination_method = optional(string, "AND")
label_vector = optional(list(object({
key = string
value = string
})))
objects = optional(list(object({})))
selected_resources = optional(list(object({
api_group = optional(string)
is_cluster_scoped = optional(bool)
kind = optional(string)
name = optional(string)
version = optional(string)
resource_list = optional(list(object({
entity_id = optional(number)
name = optional(string)
})))
})))
}))

# --- Alert policy ---
alert_policy = optional(object({
backup_run_status = list(string) # kFailure, kSuccess, kSlaViolation, kWarning
alert_targets = optional(list(object({
email_address = string
language = optional(string, "en-us")
recipient_type = optional(string, "kTo")
})))
raise_object_level_failure_alert = optional(bool)
raise_object_level_failure_alert_after_each_attempt = optional(bool)
raise_object_level_failure_alert_after_last_attempt = optional(bool)
}))

# --- SLA ---
sla = optional(list(object({
backup_run_type = optional(string, "kIncremental") # kIncremental, kFull, kLog
sla_minutes = number
})))

# --- Scheduling ---
start_time = optional(object({
hour = number
minute = number
time_zone = optional(string, "America/Los_Angeles")
}))

# --- Advanced configs (key/value pairs) ---
advanced_configs = optional(list(object({
key = string
value = string
})))

priority = optional(string, "kMedium") # kLow, kMedium, kHigh
qos_policy = optional(string) # kBackupHDD, kBackupSSD, etc.
is_paused = optional(bool, false)
abort_in_blackouts = optional(bool, false)
pause_in_blackouts = optional(bool, false)
}))
[] no
recoveries List of recovery operations to restore backups created by protection groups. When enable_recovery is true, these operations will be triggered automatically after backup completion. Supports multiple environments: Kubernetes, VMware, Physical, AWS, Azure, GCP, SQL, Oracle, and more. This variable follows the official IBM Backup Recovery provider schema and can be used across different backup scenarios. For IKS/ROKS recovery, use kubernetes_params. See the Usage section in the README for examples.
list(object({
name = string
snapshot_environment = string # kKubernetes, kVMware, kPhysical, kAWS, kAzure, kGCP, kSQL, kOracle, kView, etc.

# Kubernetes-specific recovery parameters
kubernetes_params = optional(object({
recovery_action = string # RecoverNamespaces, RecoverPVs, RecoverApps

objects = list(object({
snapshot_id = string
point_in_time_usecs = optional(number)
protection_group_id = optional(string)
protection_group_name = optional(string)
recover_from_standby = optional(bool, false)
}))
}))

# VMware-specific recovery parameters (for future provider support)
vmware_params = optional(object({
recovery_action = optional(string)
objects = optional(list(object({
snapshot_id = optional(string)
point_in_time_usecs = optional(number)
protection_group_id = optional(string)
protection_group_name = optional(string)
recover_from_standby = optional(bool, false)
})))
}))

# Physical server recovery parameters (for future provider support)
physical_params = optional(object({
recovery_action = optional(string)
objects = optional(list(object({
snapshot_id = optional(string)
point_in_time_usecs = optional(number)
protection_group_id = optional(string)
protection_group_name = optional(string)
})))
}))

# AWS-specific recovery parameters (for future provider support)
aws_params = optional(object({
recovery_action = optional(string)
objects = optional(list(object({
snapshot_id = optional(string)
point_in_time_usecs = optional(number)
protection_group_id = optional(string)
protection_group_name = optional(string)
})))
}))

# Azure-specific recovery parameters (for future provider support)
azure_params = optional(object({
recovery_action = optional(string)
objects = optional(list(object({
snapshot_id = optional(string)
point_in_time_usecs = optional(number)
protection_group_id = optional(string)
protection_group_name = optional(string)
})))
}))

# GCP-specific recovery parameters (for future provider support)
gcp_params = optional(object({
recovery_action = optional(string)
objects = optional(list(object({
snapshot_id = optional(string)
point_in_time_usecs = optional(number)
protection_group_id = optional(string)
protection_group_name = optional(string)
})))
}))

# SQL-specific recovery parameters (for future provider support)
sql_params = optional(object({
recovery_action = optional(string)
objects = optional(list(object({
snapshot_id = optional(string)
point_in_time_usecs = optional(number)
protection_group_id = optional(string)
protection_group_name = optional(string)
})))
}))

# Oracle-specific recovery parameters (for future provider support)
oracle_params = optional(object({
recovery_action = optional(string)
objects = optional(list(object({
snapshot_id = optional(string)
point_in_time_usecs = optional(number)
protection_group_id = optional(string)
protection_group_name = optional(string)
})))
}))
}))
[] no
recovery_mode Recovery mode: 'same-cluster' to restore within the same cluster, or 'cross-cluster' to restore to a different target cluster. Required when var.enable_recovery is true. string "same-cluster" no
region Region where the Backup & Recovery Service instance needs to be created. string null no
registration_images The images required for backup and recovery registration.
object({
data_mover = string
velero = string
velero_aws_plugin = string
velero_openshift_plugin = string
cohesity_dataprotect_plugin = string
init_container = optional(string, null)
})
{
"cohesity_dataprotect_plugin": "icr.io/ext/brs/cohesity-dataprotect-plugin:7.2.18@sha256:629fdf6852a9583674c41fc1ccbab3006c737067f5dccafbee2fe36fbc6ee748",
"data_mover": "icr.io/ext/brs/cohesity-datamover:7.2.18@sha256:f347698eb6180645d8c1b71d69a9fc40bb7c0bd14e5cdece9b7da79af74e8262",
"velero": "icr.io/ext/brs/oadp-velero:1.3.8@sha256:2d0014471b5c0e46cf96ac452069b9fa1ebbffd1d50a8ffecb2b443dbfbd4b00",
"velero_aws_plugin": "icr.io/ext/brs/oadp-velero-plugin-for-aws:1.3.8@sha256:3adcd0bfa963f980ad41dbff05c44c4b11d6b07e493a9c53a0ee3483a905039d",
"velero_openshift_plugin": "icr.io/ext/brs/oadp-velero-plugin-for-openshift:1.4.7@sha256:8b5dcea0fc837e5547c253f355d71b19f825eed6fac1e19c40af44b19fd7259a"
}
no
resource_tags Add user resource tags to the Backup Recovery instance to organize, track, and manage costs. list(string) [] no
rollback_on_failure Flag to automatically rollback the helm chart on installation failure. bool true no
target_cluster_id Target cluster ID for cross-cluster recovery. Required when var.recovery_mode is 'cross-cluster'. Must be a cluster already registered with the BRS instance. string null no
target_cluster_resource_group_id Resource group ID of the target cluster for cross-cluster recovery. Required when recovery_mode is 'cross-cluster'. string null no
wait_for_backup_completion Wait duration for initial backup to complete before attempting recovery. Specify with time unit suffix (e.g., '5m', '10m', '30m'). Increase this value for large clusters or slow networks. Set to '0s' to disable waiting (recovery will use existing snapshots only). string "5m" no
wait_till To avoid long wait times when you run your Terraform code, you can specify the stage when you want Terraform to mark the cluster resource creation as completed. Depending on what stage you choose, the cluster creation might not be fully completed and continues to run in the background. However, your Terraform code can continue to run without waiting for the cluster to be fully created. Supported args are MasterNodeReady, OneWorkerNodeReady, IngressReady and Normal string "Normal" no
wait_till_timeout Timeout for wait_till in minutes. number 90 no

Outputs

Name Description
backup_runs_summary Summary of backup runs per protection group. Shows run count and latest run status. Empty if var.enable_recovery is false.
brs_instance_crn CRN of the Backup & Recovery Service instance
brs_instance_guid GUID of the Backup & Recovery Service instance
brs_instance_name Name of the Backup & Recovery Service instance
brs_tags BRS tags that should be added to the cluster to prevent tag drift. Include these in your cluster's tags input.
brs_tenant_id Tenant ID of the Backup & Recovery Service instance
connection_id ID of the data source connection to the Backup & Recovery Service instance
latest_snapshots Map of protection group names to their latest snapshot IDs. Used for automatic recovery. Empty if var.enable_recovery is false.
protection_group_ids Map of protection group names to their IDs
protection_sources List of protection sources
recovery_ids Map of recovery operation names to their IDs. Empty if var.enable_recovery is false.
recovery_status Map of recovery operation names to their status information. Empty if var.enable_recovery is false.
source_registration_id ID of the registered Kubernetes source
target_cluster_id Target cluster ID for recovery operations. Same as source cluster for same-cluster recovery mode.

About

Automation for configuring backup and recovery for Kubernetes and Openshift clusters on IBM Cloud.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages