Setting up AWS Backup with Terraform
A comprehensive guide to configuring AWS Backup for centralized data protection using Terraform Infrastructure as Code
Setting up AWS Backup with Terraform
AWS Backup is a fully managed backup service that makes it easy to centralize and automate data protection across AWS services. This guide shows how to set up AWS Backup using Terraform.
Prerequisites
- AWS CLI configured
- Terraform installed
- Understanding of backup requirements
- Basic knowledge of AWS services
Project Structure
aws-backup-terraform/
├── main.tf
├── variables.tf
├── outputs.tf
└── terraform.tfvars
Basic AWS Backup Configuration
# main.tf
provider "aws" {
region = var.aws_region
}
# AWS Backup Vault
resource "aws_backup_vault" "main" {
name = "${var.project_name}-vault"
kms_key_arn = aws_kms_key.backup.arn
tags = {
Environment = var.environment
}
}
# KMS Key for Encryption
resource "aws_kms_key" "backup" {
description = "KMS key for AWS Backup encryption"
deletion_window_in_days = 7
enable_key_rotation = true
tags = {
Environment = var.environment
}
}
# IAM Role for AWS Backup
resource "aws_iam_role" "backup" {
name = "${var.project_name}-backup-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "backup.amazonaws.com"
}
}
]
})
}
# IAM Policy for AWS Backup
resource "aws_iam_role_policy_attachment" "backup" {
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSBackupServiceRolePolicyForBackup"
role = aws_iam_role.backup.name
}
# Backup Plan
resource "aws_backup_plan" "main" {
name = "${var.project_name}-backup-plan"
rule {
rule_name = "daily-backup"
target_vault_name = aws_backup_vault.main.name
schedule = "cron(0 5 ? * * *)"
lifecycle {
delete_after = 30
}
copy_action {
destination_vault_arn = aws_backup_vault.secondary.arn
}
}
rule {
rule_name = "weekly-backup"
target_vault_name = aws_backup_vault.main.name
schedule = "cron(0 5 ? * SAT *)"
lifecycle {
cold_storage_after = 30
delete_after = 90
}
}
rule {
rule_name = "monthly-backup"
target_vault_name = aws_backup_vault.main.name
schedule = "cron(0 5 1 * ? *)"
lifecycle {
cold_storage_after = 90
delete_after = 365
}
}
advanced_backup_setting {
backup_options = {
WindowsVSS = "enabled"
}
resource_type = "EC2"
}
tags = {
Environment = var.environment
}
}
# Secondary Backup Vault (for cross-region copies)
resource "aws_backup_vault" "secondary" {
provider = aws.secondary
name = "${var.project_name}-vault-secondary"
kms_key_arn = aws_kms_key.backup_secondary.arn
tags = {
Environment = var.environment
}
}
# Secondary KMS Key
resource "aws_kms_key" "backup_secondary" {
provider = aws.secondary
description = "KMS key for secondary AWS Backup encryption"
deletion_window_in_days = 7
enable_key_rotation = true
tags = {
Environment = var.environment
}
}
# Backup Selection
resource "aws_backup_selection" "main" {
name = "${var.project_name}-backup-selection"
iam_role_arn = aws_iam_role.backup.arn
plan_id = aws_backup_plan.main.id
selection_tag {
type = "STRINGEQUALS"
key = "Backup"
value = "true"
}
condition {
string_equals {
key = "aws:ResourceTag/Environment"
value = var.environment
}
}
resources = var.resource_arns
}
Variables Configuration
# variables.tf
variable "aws_region" {
description = "AWS region"
type = string
default = "us-west-2"
}
variable "project_name" {
description = "Project name"
type = string
}
variable "environment" {
description = "Environment name"
type = string
default = "dev"
}
variable "resource_arns" {
description = "List of resource ARNs to backup"
type = list(string)
default = []
}
variable "secondary_region" {
description = "Secondary region for backup copies"
type = string
default = "us-east-1"
}
Best Practices
-
Backup Management
- Define retention policies
- Implement cross-region copies
- Configure notifications
- Regular backup testing
-
Security
- Enable encryption
- Use proper IAM roles
- Implement access policies
- Regular security reviews
-
Cost Optimization
- Monitor storage usage
- Use lifecycle policies
- Clean up old backups
- Regular cost reviews
-
Performance
- Schedule during off-peak
- Monitor backup windows
- Use incremental backups
- Regular performance reviews
Cross-Account Backup
# Cross-Account Backup Vault Policy
resource "aws_backup_vault_policy" "cross_account" {
backup_vault_name = aws_backup_vault.main.name
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowCrossAccountBackup"
Effect = "Allow"
Principal = {
AWS = var.cross_account_arns
}
Action = [
"backup:CopyIntoBackupVault"
]
Resource = "*"
}
]
})
}
# Cross-Account IAM Role
resource "aws_iam_role" "cross_account_backup" {
provider = aws.source
name = "${var.project_name}-cross-account-backup"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
AWS = var.target_account_arn
}
}
]
})
}
Notification Configuration
# SNS Topic for Notifications
resource "aws_sns_topic" "backup_notifications" {
name = "${var.project_name}-backup-notifications"
tags = {
Environment = var.environment
}
}
# SNS Topic Policy
resource "aws_sns_topic_policy" "backup_notifications" {
arn = aws_sns_topic.backup_notifications.arn
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowBackupPublish"
Effect = "Allow"
Principal = {
Service = "backup.amazonaws.com"
}
Action = "SNS:Publish"
Resource = aws_sns_topic.backup_notifications.arn
}
]
})
}
# EventBridge Rule for Backup Events
resource "aws_cloudwatch_event_rule" "backup_events" {
name = "${var.project_name}-backup-events"
description = "Capture AWS Backup events"
event_pattern = jsonencode({
source = ["aws.backup"]
detail-type = ["AWS Backup Job State Change"]
})
}
# EventBridge Target
resource "aws_cloudwatch_event_target" "sns" {
rule = aws_cloudwatch_event_rule.backup_events.name
target_id = "SendToSNS"
arn = aws_sns_topic.backup_notifications.arn
input_transformer {
input_paths = {
status = "$.detail.state"
resource = "$.detail.resourceArn"
backup_job = "$.detail.backupJobId"
}
input_template = "\"Backup job <backup_job> for resource <resource> changed status to <status>\""
}
}
Monitoring Configuration
# CloudWatch Dashboard
resource "aws_cloudwatch_dashboard" "backup" {
dashboard_name = "${var.project_name}-backup-dashboard"
dashboard_body = jsonencode({
widgets = [
{
type = "metric"
x = 0
y = 0
width = 12
height = 6
properties = {
metrics = [
["AWS/Backup", "NumberOfBackupJobsCompleted", "BackupVaultName", aws_backup_vault.main.name],
["AWS/Backup", "NumberOfBackupJobsFailed", "BackupVaultName", aws_backup_vault.main.name]
]
period = 300
stat = "Sum"
region = var.aws_region
title = "Backup Job Status"
}
}
]
})
}
# CloudWatch Alarms
resource "aws_cloudwatch_metric_alarm" "backup_failures" {
alarm_name = "${var.project_name}-backup-failures"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = "1"
metric_name = "NumberOfBackupJobsFailed"
namespace = "AWS/Backup"
period = "300"
statistic = "Sum"
threshold = "0"
alarm_description = "This metric monitors backup job failures"
alarm_actions = [aws_sns_topic.backup_notifications.arn]
dimensions = {
BackupVaultName = aws_backup_vault.main.name
}
}
Common Use Cases
- Database Backup
resource "aws_backup_selection" "databases" {
name = "${var.project_name}-database-backup"
iam_role_arn = aws_iam_role.backup.arn
plan_id = aws_backup_plan.main.id
selection_tag {
type = "STRINGEQUALS"
key = "Type"
value = "Database"
}
resources = [
aws_db_instance.main.arn,
aws_rds_cluster.aurora.arn
]
}
- EC2 Instance Backup
resource "aws_backup_selection" "ec2" {
name = "${var.project_name}-ec2-backup"
iam_role_arn = aws_iam_role.backup.arn
plan_id = aws_backup_plan.main.id
selection_tag {
type = "STRINGEQUALS"
key = "Backup"
value = "true"
}
condition {
string_equals {
key = "aws:ResourceTag/Environment"
value = var.environment
}
}
resources = [
"arn:aws:ec2:${var.aws_region}:${data.aws_caller_identity.current.account_id}:instance/*"
]
}
Advanced Features
# Continuous Backup
resource "aws_backup_plan" "continuous" {
name = "${var.project_name}-continuous-backup"
rule {
rule_name = "continuous-backup"
target_vault_name = aws_backup_vault.main.name
schedule = "cron(0/15 * ? * * *)"
lifecycle {
delete_after = 7
}
enable_continuous_backup = true
}
tags = {
Environment = var.environment
}
}
# Backup Report Plan
resource "aws_backup_report_plan" "main" {
name = "${var.project_name}-backup-report"
description = "Daily backup reports"
report_delivery_channel {
formats = ["CSV"]
s3_bucket_name = aws_s3_bucket.backup_reports.id
}
report_setting {
report_template = "BACKUP_JOB_REPORT"
framework_arns = [aws_backup_framework.main.arn]
}
tags = {
Environment = var.environment
}
}
Conclusion
This setup provides a comprehensive foundation for deploying AWS Backup using Terraform. Remember to:
- Plan your backup strategy carefully
- Implement proper retention policies
- Monitor backup status and costs
- Keep your configurations versioned
- Test backup restoration regularly
The complete code can be customized based on your specific requirements and use cases.