In my previous post, Creating Secure Backups for DynamoDB Tables with Terraform, I discussed how to create DynamoDB table backups within the same AWS account. In this post, I will explain how to create cross-account DynamoDB backups, where the tables are in one AWS account and the backups are created in another AWS account for enhanced security and limited access.
Steps to Configure Cross-Account Backups
To achieve cross-account DynamoDB backups, follow these steps:
Step 1: Create Destination Vault in Backup Account
First, set up the backup vault in the AWS account where you want to store the backups. This includes configuring a KMS key for encryption and setting up the vault policy to allow cross-account access.
resource "aws_backup_vault" "destination_backup_vault" {
name = "destination-backup-vault"
kms_key_arn = aws_kms_key.backup_vault_key.arn
}
resource "aws_kms_key" "backup_vault_key" {
description = "KMS key for backup vault encryption"
enable_key_rotation = true
}
resource "aws_backup_vault_policy" "destination_backup_vault_policy" {
backup_vault_name = aws_backup_vault.destination_backup_vault.name
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Sid : "AllowCrossAccountAccess",
Effect = "Allow",
Principal = {
AWS = "arn:aws:iam::[YOUR SOURCE ACCOUNT ID]:root"
},
Action = [
"backup:CopyIntoBackupVault"
],
Resource = "*"
}
]
})
}
Step 2: Create Source Vault in Source Account
Next, create the backup vault in the source account where the DynamoDB tables reside.
resource "aws_backup_vault" "source_backup_vault" {
name = "source-backup-vault"
kms_key_arn = aws_kms_key.backup_vault_key.arn
}
resource "aws_kms_key" "backup_vault_key" {
description = "KMS Key for Backup"
enable_key_rotation = true
}
Step 3: Create IAM Role with Relevant Permissions
Create an IAM role in the source account with the necessary permissions to perform backups, including the AWSBackupServiceRolePolicyForBackup
policy.
data "aws_iam_policy_document" "assume_role" {
statement {
effect = "Allow"
principals {
type = "Service"
identifiers = ["backup.amazonaws.com"]
}
actions = ["sts:AssumeRole"]
}
}
resource "aws_iam_role" "source_backup_role" {
name = "source-backup-role"
assume_role_policy = data.aws_iam_policy_document.assume_role.json
inline_policy {
name = "backup-policy"
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Action = [
"dynamodb:CreateBackup",
"dynamodb:DeleteBackup",
"dynamodb:DescribeBackup",
"dynamodb:ListBackups",
"dynamodb:ListTables",
"dynamodb:RestoreTableFromBackup",
"dynamodb:ListTagsOfResource",
"dynamodb:StartAwsBackupJob",
"dynamodb:RestoreTableFromAwsBackup"
],
Resource = "*"
},
{
Effect = "Allow",
Action = [
"backup:StartBackupJob",
"backup:StopBackupJob",
"backup:TagResource",
"backup:UntagResource"
],
Resource = "*"
}
]
})
}
}
resource "aws_iam_role_policy_attachment" "service_backup_policy" {
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSBackupServiceRolePolicyForBackup"
role = aws_iam_role.source_backup_role.name
}
Step 4: Create a Backup Plan
Configure a backup plan to periodically back up the DynamoDB tables and copy them to the destination vault.
resource "aws_backup_plan" "cross_account_cross_region_copy" {
name = "cross-account-cross-region-copy"
rule {
rule_name = "copy-to-destination"
target_vault_name = aws_backup_vault.source_backup_vault.name
schedule = "cron(38 13 * * ? *)"
copy_action {
destination_vault_arn = var.destination_backup_vault_arn
lifecycle {
delete_after = 30 # Retain for 30 days
}
}
}
}
resource "aws_backup_selection" "dynamodb_cross_account_copy_selection" {
plan_id = aws_backup_plan.cross_account_cross_region_copy.id
name = "copy-backup-selection"
iam_role_arn = aws_iam_role.source_backup_role.arn
resources = [
//table arns
]
}
resource "aws_backup_vault_policy" "source_backup_vault_policy" {
backup_vault_name = aws_backup_vault.source_backup_vault.name
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Sid : "AllowCrossAccountAccess",
Effect = "Allow",
Principal = {
AWS = "arn:aws:iam::[YOUR BACKUP ACCOUNT ID]:root"
},
Action = [
"backup:CopyFromBackupVault",
"backup:CopyIntoBackupVault"
],
Resource = "*"
}
]
})
}
So your complete destination terraform file would look something like this
resource "aws_backup_vault" "source_backup_vault" {
name = "source-backup-vault"
kms_key_arn = aws_kms_key.backup_vault_key.arn
}
resource "aws_kms_key" "backup_vault_key" {
description = "KMS Key for Backup"
enable_key_rotation = true
}
resource "aws_backup_vault_policy" "source_backup_vault_policy" {
backup_vault_name = aws_backup_vault.source_backup_vault.name
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Sid : "AllowCrossAccountAccess",
Effect = "Allow",
Principal = {
AWS = "arn:aws:iam::[YOUR BACKUP ACCOUNT ID]:root"
},
Action = [
"backup:CopyFromBackupVault",
"backup:CopyIntoBackupVault"
],
Resource = "*"
}
]
})
}
data "aws_iam_policy_document" "assume_role" {
statement {
effect = "Allow"
principals {
type = "Service"
identifiers = ["backup.amazonaws.com"]
}
actions = ["sts:AssumeRole"]
}
}
resource "aws_iam_role" "source_backup_role" {
name = "source-backup-role"
assume_role_policy = data.aws_iam_policy_document.assume_role.json
inline_policy {
name = "backup-policy"
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Action = [
"dynamodb:CreateBackup",
"dynamodb:DeleteBackup",
"dynamodb:DescribeBackup",
"dynamodb:ListBackups",
"dynamodb:ListTables",
"dynamodb:RestoreTableFromBackup",
"dynamodb:ListTagsOfResource",
"dynamodb:StartAwsBackupJob",
"dynamodb:RestoreTableFromAwsBackup"
],
Resource = "*"
},
{
Effect = "Allow",
Action = [
"backup:StartBackupJob",
"backup:StopBackupJob",
"backup:TagResource",
"backup:UntagResource"
],
Resource = "*"
}
]
})
}
}
resource "aws_iam_role_policy_attachment" "service_backup_policy" {
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSBackupServiceRolePolicyForBackup"
role = aws_iam_role.source_backup_role.name
}
resource "aws_backup_plan" "cross_account_cross_region_copy" {
name = "cross-account-cross-region-copy"
rule {
rule_name = "copy-to-destination"
target_vault_name = aws_backup_vault.source_backup_vault.name
schedule = "cron(38 13 * * ? *)"
copy_action {
destination_vault_arn = var.destination_backup_vault_arn
lifecycle {
delete_after = 30 # Retain for 30 days
}
}
}
}
resource "aws_backup_selection" "dynamodb_cross_account_copy_selection" {
plan_id = aws_backup_plan.cross_account_cross_region_copy.id
name = "copy-backup-selection"
iam_role_arn = aws_iam_role.source_backup_role.arn
resources = [
//table arns
]
}
Understanding the Cron Schedule
The schedule
parameter in the backup plan uses a cron expression to determine when the backup should run. Here is an explanation of the cron expression used in this configuration:
schedule = "cron(38 13 * * ? *)"
- 38: Minute (38th minute of the hour).
- 13: Hour (1 PM UTC).
- *: Day of the month (any day).
- *: Month (any month).
- ?: Day of the week (any day of the week).
- *: Year (optional, any year).
This configuration schedules the backup job to run daily at 1:38 PM UTC.
Conclusion
Creating cross-account backups for DynamoDB tables enhances the security and availability of your data. By following the steps outlined in this guide, you can use Terraform to automate the process of setting up secure backups across different AWS accounts. This approach ensures that your backups are stored securely in a separate account, providing an additional layer of protection against data loss or unauthorized access.
If you have any suggestions or improvements, please feel free to comment. Happy Terraforming!
Top comments (0)