# Automated S3 Backup Tool

This guide demonstrates how to automate file backups to Amazon S3 using the `boto3` library. It supports backing up individual files or recursively uploading entire directories while preserving the folder structure.

**Modules Used:**
*   `boto3`: The Amazon Web Services (AWS) SDK for Python.
*   `botocore`: Used for handling AWS-specific exceptions.
*   [[programming/python/modules/argparse-module|argparse]]: To handle command-line arguments.
*   [[programming/python/modules/pathlib-module|pathlib]]: For robust file path handling.

## Prerequisites

1.  **AWS Account**: You need an active AWS account.
2.  **S3 Bucket**: Create a bucket in S3 (e.g., `my-backup-bucket`).
3.  **IAM User**: Create an IAM user with `AmazonS3FullAccess` (or specific write permissions).
4.  **Configuration**: Configure your credentials locally.
    *   Install AWS CLI: `pip install awscli`
    *   Run: `aws configure` (Enter Key ID, Secret Key, Region).

## Installation

```bash
pip install boto3
```

## The Code

Save this as `s3_backup.py`.

```python
import boto3
from botocore.exceptions import ClientError
import argparse
import os
from pathlib import Path
import sys

def upload_file(s3_client, file_name, bucket, object_name):
    """Uploads a single file to S3."""
    try:
        s3_client.upload_file(file_name, bucket, object_name)
        print(f"Uploaded: {file_name} -> s3://{bucket}/{object_name}")
        return True
    except ClientError as e:
        print(f"Error uploading {file_name}: {e}")
        return False

def backup_to_s3(source, bucket, prefix=""):
    source_path = Path(source).resolve()
    
    if not source_path.exists():
        print(f"Error: Source '{source}' not found.")
        return

    # Initialize S3 client once
    s3 = boto3.client('s3')
    
    files_to_upload = []

    if source_path.is_file():
        # Single file backup
        # Use the filename as the object key, optionally prepended with a prefix
        s3_key = os.path.join(prefix, source_path.name).replace("\\", "/")
        files_to_upload.append((str(source_path), s3_key))
    else:
        # Directory backup (Recursive)
        print(f"Scanning '{source}'...")
        for file_path in source_path.rglob("*"):
            if file_path.is_file():
                # Calculate relative path to maintain folder structure in S3
                # e.g., /home/user/data/img.png -> data/img.png
                rel_path = file_path.relative_to(source_path.parent)
                s3_key = os.path.join(prefix, rel_path).replace("\\", "/")
                files_to_upload.append((str(file_path), s3_key))

    print(f"Found {len(files_to_upload)} files to upload to bucket '{bucket}'.")
    
    success_count = 0
    for local_file, s3_key in files_to_upload:
        if upload_file(s3, local_file, bucket, s3_key):
            success_count += 1
            
    print(f"\nBackup complete. Uploaded {success_count}/{len(files_to_upload)} files.")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="S3 Backup Utility")
    parser.add_argument("source", help="Local file or directory to backup")
    parser.add_argument("bucket", help="Target S3 Bucket name")
    parser.add_argument("--prefix", default="", help="S3 Folder prefix (optional)")
    
    args = parser.parse_args()
    
    backup_to_s3(args.source, args.bucket, args.prefix)
```

## Usage

```bash
# Backup a single file
python s3_backup.py important_doc.pdf my-backup-bucket

# Backup a directory to a specific folder in S3
python s3_backup.py ./projects my-backup-bucket --prefix backups/2023
```

[[programming/python/python]]