Backing up all your EBS volumes using Terraform
Tags: terraform, ebs, aws, backup
Published: 2023-01-23 20:37:11

Looking for a quick way to back up all your EBS volumes using Terraform? Look no further.

This quick guide will help you set up an AWS Backup plan for EBS volumes, with a schedule and some rules for lifecycle management, which can be easily extended to any other supported resource.  

First, let's get all our EBS volumes using the old trusty data resource. 

```
data "aws_ebs_volumes" "volumes" {
  filter {
    name   = "tag:Name"
    values = ["*"]
  }
}

```
With the code above, you can fetch all your volumes and filter it by tag; this still won't be enough, as we need to build a list of _ARNs_ that Terraform can iterate, and AWS can use it to set up a backup plan and schedule accordingly. 

Let's continue by adding another filter to get the volume details. Again, this is required for setting up the backup plan correctly.

```
data "aws_ebs_volume" "data" {
  for_each = toset(data.aws_ebs_volumes.volumes.ids)
  filter {
    name   = "volume-id"
    values = [each.value]
  }
}

```

Ok, that's looking better; this list can now be fed to AWS backup plans, and it should be enough in most cases. One minor drawback with this approach is that if your EBS inventory changes constantly, you'll need to run this often to scan for new volumes. 

Now on to the setup. Let's first set up some permissions to make everything work together. 

```
resource "aws_iam_role" "ebs_aws_backup_role" {
  name = "${local.prefix}-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Sid    = "AssumeServiceRole"
        Principal = {
          Service = "backup.amazonaws.com"
        }
      },
    ]
  })

  tags = local.tags
}

resource "aws_iam_role_policy_attachment" "ebs_aws_backup_service_role" {
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSBackupServiceRolePolicyForBackup"
  role       = aws_iam_role.ebs_aws_backup_role.name
}
```

With this config out of the way, we should be able to continue with the setup of the vault


```
resource "aws_backup_vault" "ebs_backup_vault" {
  name = "${local.prefix}-vault"
  tags = local.tags
}
```

And now, let's move to the most critical piece, the backup plan; of course, you can adjust your parameters according to your use case; one working example can be seen below.

```
resource "aws_backup_plan" "ebs_backup_plan" {
  name = "${local.prefix}-plan"

  rule {
    rule_name         = "${local.prefix}-rule-${local.backups.retention}-day"
    target_vault_name = aws_backup_vault.ebs_backup_vault.name
    schedule          = local.backups.schedule
    start_window      = 60
    completion_window = 300

    lifecycle {
      cold_storage_after = local.backups.cold_storage_after
      delete_after       = local.backups.retention
    }
  }

  tags = local.tags
}
```

Finally, let's enable the backup plan for the resources we got on the first step. 

```
resource "aws_backup_selection" "ebs_backup_selection" {
  iam_role_arn = aws_iam_role.ebs_aws_backup_role.arn
  name         = "${local.prefix}-selection"
  plan_id      = aws_backup_plan.ebs_backup_plan.id

  resources = [for v in data.aws_ebs_volume.data : v.arn]
}
```

`aws_backup_selection` supports filtering by tag in case you want to be very specific about which resource you want to be part of the plan. Consider doing that instead if you have all your resources properly tagged. 

And that's it. You should be able to apply a Terraform valid plan and have your EBS backed up automatically.