Files

4.8 KiB

Assignment

The Nautilus DevOps team is currently engaged in a cleanup process, focusing on removing unnecessary data and services from their AWS account. As part of the migration process, several resources were created for one-time use only, necessitating a cleanup effort to optimize their AWS environment.

A S3 bucket named devops-bck-28889 already exists.

  1. Copy the contents of devops-bck-28889 S3 bucket to /opt/s3-backup/ directory on terraform-client host (the landing host once you load this lab).

  2. Delete the S3 bucket devops-bck-28889.

  3. Use the AWS CLI through Terraform to accomplish this task—for example, by running AWS CLI commands within Terraform. The Terraform working directory is /home/bob/terraform. Update the main.tf file (do not create a separate .tf file) to accomplish this task.

Note: Right-click under the EXPLORER section in VS Code and select Open in Integrated Terminal to launch the terminal.

Solution

S3 Backup + Delete via Terraform — devops-bck-28889

This task runs AWS CLI commands through Terraform using a null_resource with a local-exec provisioner: sync the bucket's contents to a local directory on the terraform-client host, then delete the bucket.

main.tf (append this block)

terraform {
  required_providers {
    null = {
      source  = "hashicorp/null"
      version = "~> 3.2"
    }
  }
}

resource "null_resource" "s3_backup_and_delete" {
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = <<-EOT
      mkdir -p /opt/s3-backup/ && \
      aws s3 sync s3://devops-bck-28889 /opt/s3-backup/ && \
      aws s3 rb s3://devops-bck-28889 --force
    EOT
  }
}

How to run

cd /home/bob/terraform
terraform init
terraform apply -auto-approve

How it works

Why a null_resource + local-exec

Terraform has no native resource for "copy bucket contents to a local path" or for a force-delete of a populated bucket. When a task explicitly says use the AWS CLI through Terraform, the idiomatic pattern is a null_resource whose local-exec provisioner shells out to the CLI on the machine running Terraform (here, the terraform-client landing host, which already has AWS credentials configured).

  • null_resource — a resource that does nothing itself; it exists solely to hang provisioners on. Creating it once (on apply) triggers its local-exec block.
  • interpreter = ["/bin/bash", "-c"] — runs the script under bash. (The default is /bin/sh; bash is set explicitly for predictable && chaining and heredoc behavior.)

The command sequence

The three steps are chained with &&, so each runs only if the previous succeeded — critical because step 3 is destructive:

  1. mkdir -p /opt/s3-backup/ — ensures the destination directory exists (and is a no-op if it already does).
  2. aws s3 sync s3://devops-bck-28889 /opt/s3-backup/ — copies every object from the bucket into the local directory, preserving key paths. sync only transfers what's missing/changed, but on a fresh directory it pulls everything.
  3. aws s3 rb s3://devops-bck-28889 --force — removes the bucket. rb (remove bucket) normally refuses a non-empty bucket; --force first deletes all objects, then the bucket itself.

The && chaining is the safety mechanism: if the sync fails (network, permissions), the rb never executes, so you don't delete data you failed to back up.

Run-once semantics

A null_resource runs its provisioner when it's created. After a successful apply it's in state and won't re-run on subsequent applies. That's the desired behavior for a one-time cleanup. If you ever needed to run it again, you'd terraform taint null_resource.s3_backup_and_delete (or terraform apply -replace=...) to force recreation — but note the bucket is gone after the first run, so a re-run would fail at the sync step anyway.

Modern alternative

Terraform 1.4+ ships a built-in terraform_data resource that replaces null_resource without needing the hashicorp/null provider. The same block written with it:

resource "terraform_data" "s3_backup_and_delete" {
  provisioner "local-exec" {
    interpreter = ["/bin/bash", "-c"]
    command     = <<-EOT
      mkdir -p /opt/s3-backup/ && \
      aws s3 sync s3://devops-bck-28889 /opt/s3-backup/ && \
      aws s3 rb s3://devops-bck-28889 --force
    EOT
  }
}

Either works; null_resource is shown as the primary since it's the widely-recognized form.

Verify

# Backup landed locally
ls -la /opt/s3-backup/

# Bucket is gone (this should error with "NoSuchBucket" / "Not Found")
aws s3 ls s3://devops-bck-28889 || echo "Bucket deleted."

Expected — /opt/s3-backup/ contains the former bucket contents, and the s3 ls against the bucket fails because it no longer exists.