Files
BC-bak/README.md

562 lines
14 KiB
Markdown
Raw Normal View History

2026-02-09 18:57:39 +01:00
# Business Central SaaS Automated Backup System
Comprehensive backup solution for Microsoft Dynamics 365 Business Central SaaS that:
- Exports database via BC Admin Center API every hour
- Encrypts backups with GPG (AES-256)
- Uploads to S3-compatible object storage
- Enables immutability with 30-day delete prevention
- Maintains timestamped backup history
## Features
- **Automated Hourly Backups**: Uses cron/systemd to run backups on schedule
- **Secure Encryption**: GPG encryption with AES-256 cipher
- **Immutable Storage**: S3 Object Lock (WORM) with 30-day retention
- **Multiple S3 Providers**: AWS S3, MinIO, Wasabi, Backblaze B2
- **Comprehensive Logging**: Detailed logs for troubleshooting
- **Error Handling**: Retries and proper error reporting
- **Clean Architecture**: Modular bash + PowerShell scripts
## Architecture
```
┌─────────────────────┐
│ Cron/Systemd │
│ (Hourly Trigger) │
└──────────┬──────────┘
┌─────────────────────┐
│ bc-backup.sh │ ◄─── Main orchestration script (bash)
│ - Orchestrates │
│ - Encryption │
│ - S3 Upload │
└──────────┬──────────┘
┌─────────────────────┐
│ bc-export.ps1 │ ◄─── BC export logic (PowerShell)
│ - Azure AD auth │
│ - API calls │
│ - Download BACPAC │
└──────────┬──────────┘
┌─────────────────────┐
│ BC Admin API │
│ (Microsoft) │
└─────────────────────┘
```
## Prerequisites
### 1. System Requirements
- **Linux server** (Ubuntu 20.04+, Debian 10+, CentOS 7+, or similar)
- **PowerShell 7+** (installed automatically by setup script)
- **GPG** (for encryption)
- **AWS CLI v2** or **s3cmd** (for S3 uploads)
- **Root or sudo access** (for initial setup)
### 2. Business Central Requirements
- Active Business Central SaaS subscription (paid, not trial)
- Production environment (exports only available from Production)
- Admin access to BC Admin Center
### 3. Azure AD App Registration
You need an Azure AD application with API permissions to access BC Admin Center API.
### 4. S3-Compatible Storage
- S3 bucket with **Object Lock enabled** (immutability)
- Access credentials (Access Key ID + Secret Access Key)
## Quick Start
### 1. Download and Setup
```bash
cd /home/malin/c0ding/bcbak
chmod +x setup.sh
./setup.sh
```
The setup script will:
- Check and install dependencies (PowerShell, GPG, AWS CLI)
- Create directory structure
- Copy configuration template
- Set proper permissions
### 2. Create Azure AD App Registration
#### Step-by-Step:
1. Navigate to [Azure Portal](https://portal.azure.com)
2. Go to **Azure Active Directory** > **App registrations** > **New registration**
3. Configure:
- **Name**: `BC-Backup-Service`
- **Supported account types**: Accounts in this organizational directory only
- **Redirect URI**: Leave empty
4. Click **Register**
5. Note the following from the Overview page:
- **Application (client) ID**
- **Directory (tenant) ID**
#### Create Client Secret:
1. Go to **Certificates & secrets** > **New client secret**
2. **Description**: `BC Backup Key`
3. **Expires**: Choose appropriate duration (6 months, 1 year, etc.)
4. Click **Add**
5. **Copy the secret value immediately** (shown only once!)
#### Add API Permissions:
1. Go to **API permissions** > **Add a permission**
2. Select **Dynamics 365 Business Central**
3. Choose **Application permissions** (not Delegated)
4. Select: `Automation.ReadWrite.All` or `API.ReadWrite.All`
5. Click **Add permissions**
6. **Important**: Click **Grant admin consent for [Your Organization]**
- Requires Global Administrator role
### 3. Configure S3 Bucket with Object Lock
#### AWS S3 Example:
```bash
# Create bucket with Object Lock enabled
aws s3api create-bucket \
--bucket my-bc-backups \
--region us-east-1 \
--object-lock-enabled-for-bucket
# Configure default retention (30 days, COMPLIANCE mode)
aws s3api put-object-lock-configuration \
--bucket my-bc-backups \
--object-lock-configuration '{
"ObjectLockEnabled": "Enabled",
"Rule": {
"DefaultRetention": {
"Mode": "COMPLIANCE",
"Days": 30
}
}
}'
```
#### MinIO Example:
```bash
# Create bucket
mc mb myminio/my-bc-backups --with-lock
# Set retention
mc retention set --default COMPLIANCE "30d" myminio/my-bc-backups
```
**Important**: Object Lock can **only be enabled when creating a bucket**. You cannot add it to existing buckets.
### 4. Configure the Backup System
Edit the configuration file:
```bash
nano bc-backup.conf
```
Fill in the required values:
```bash
# Azure AD Configuration
AZURE_TENANT_ID="your-tenant-id-here"
AZURE_CLIENT_ID="your-client-id-here"
AZURE_CLIENT_SECRET="your-client-secret-here"
# Business Central
BC_ENVIRONMENT_NAME="Production"
# Generate a strong encryption passphrase
ENCRYPTION_PASSPHRASE="$(openssl rand -base64 32)"
# S3 Configuration
S3_BUCKET="my-bc-backups"
S3_ENDPOINT="https://s3.amazonaws.com" # or your S3 provider
AWS_ACCESS_KEY_ID="your-access-key"
AWS_SECRET_ACCESS_KEY="your-secret-key"
AWS_DEFAULT_REGION="us-east-1"
# Backup settings
RETENTION_DAYS="30"
S3_TOOL="awscli"
CLEANUP_LOCAL="true"
```
**Security**: Store your `ENCRYPTION_PASSPHRASE` securely in a password manager!
### 5. Test Manual Backup
Run a test backup:
```bash
./bc-backup.sh
```
Monitor the logs:
```bash
tail -f logs/backup.log
```
Expected output:
```
[2026-01-07 10:00:00] =========================================
[2026-01-07 10:00:00] Starting Business Central backup process
[2026-01-07 10:00:00] =========================================
[2026-01-07 10:00:00] Environment: Production
[2026-01-07 10:00:00] S3 Bucket: my-bc-backups
[2026-01-07 10:00:00] Retention: 30 days
[2026-01-07 10:00:01] Step 1: Initiating database export via BC Admin Center API
...
```
### 6. Set Up Automated Hourly Backups
#### Option A: Using Cron (Simpler)
```bash
crontab -e
```
Add this line for hourly backups:
```
0 * * * * /home/malin/c0ding/bcbak/bc-backup.sh >> /home/malin/c0ding/bcbak/logs/cron.log 2>&1
```
See `cron-examples.txt` for more scheduling options.
#### Option B: Using Systemd Timers (More Reliable)
Create service file:
```bash
sudo nano /etc/systemd/system/bc-backup.service
```
```ini
[Unit]
Description=Business Central Database Backup
[Service]
Type=oneshot
User=malin
WorkingDirectory=/home/malin/c0ding/bcbak
ExecStart=/home/malin/c0ding/bcbak/bc-backup.sh
StandardOutput=append:/home/malin/c0ding/bcbak/logs/backup.log
StandardError=append:/home/malin/c0ding/bcbak/logs/backup.log
```
Create timer file:
```bash
sudo nano /etc/systemd/system/bc-backup.timer
```
```ini
[Unit]
Description=Run BC Backup Every Hour
[Timer]
OnCalendar=hourly
Persistent=true
[Install]
WantedBy=timers.target
```
Enable and start:
```bash
sudo systemctl daemon-reload
sudo systemctl enable bc-backup.timer
sudo systemctl start bc-backup.timer
sudo systemctl status bc-backup.timer
```
## File Structure
```
bcbak/
├── bc-backup.sh # Main orchestration script
├── bc-export.ps1 # PowerShell BC export logic
├── bc-backup.conf # Your configuration (gitignored)
├── bc-backup.conf.template # Configuration template
├── setup.sh # Installation script
├── cron-examples.txt # Cron scheduling examples
├── README.md # This file
├── logs/ # Backup logs
│ ├── backup.log
│ └── cron.log
└── temp/ # Temporary files (auto-cleaned)
└── bc_backup_*.bacpac
```
## How It Works
### 1. Database Export (bc-export.ps1)
- Authenticates to Azure AD using client credentials (OAuth 2.0)
- Calls BC Admin Center API to initiate database export
- Polls for completion (exports can take 15-60 minutes)
- Downloads BACPAC file to local temp directory
### 2. Encryption (bc-backup.sh)
- Uses GPG with AES-256 symmetric encryption
- Encrypts the BACPAC file with your passphrase
- Original unencrypted file is deleted
### 3. Upload to S3
- Uploads encrypted file with timestamp in filename
- Format: `backups/bc_backup_Production_20260107_100000.bacpac.gpg`
- Sets Object Lock retention (COMPLIANCE mode, 30 days)
- Files are **immutable** and **cannot be deleted** until retention expires
### 4. Verification & Cleanup
- Verifies upload success
- Removes local encrypted file (optional)
- Logs all operations
## Restoring from Backup
### 1. Download Encrypted Backup
```bash
# Using AWS CLI
aws s3 cp \
s3://my-bc-backups/backups/bc_backup_Production_20260107_100000.bacpac.gpg \
./backup.bacpac.gpg \
--endpoint-url https://s3.amazonaws.com
```
### 2. Decrypt the Backup
```bash
# Enter your ENCRYPTION_PASSPHRASE when prompted
gpg --decrypt backup.bacpac.gpg > backup.bacpac
```
### 3. Restore to Azure SQL Database
```bash
# Using SqlPackage (download from Microsoft)
sqlpackage /a:Import \
/sf:backup.bacpac \
/tsn:your-server.database.windows.net \
/tdn:RestoredDatabase \
/tu:admin \
/tp:password
```
### 4. Connect BC to Restored Database
Contact Microsoft Support to point your BC environment to the restored database.
## Monitoring and Maintenance
### Check Backup Logs
```bash
# View latest backup log
tail -100 logs/backup.log
# Follow live log
tail -f logs/backup.log
# Check for errors
grep ERROR logs/backup.log
```
### List S3 Backups
```bash
# AWS CLI
aws s3 ls s3://my-bc-backups/backups/ --endpoint-url https://s3.amazonaws.com
# s3cmd
s3cmd ls s3://my-bc-backups/backups/
```
### Check Object Lock Status
```bash
aws s3api get-object-retention \
--bucket my-bc-backups \
--key backups/bc_backup_Production_20260107_100000.bacpac.gpg \
--endpoint-url https://s3.amazonaws.com
```
### Verify Cron/Timer Status
```bash
# Cron
crontab -l
grep CRON /var/log/syslog | tail
# Systemd
systemctl status bc-backup.timer
journalctl -u bc-backup.service -n 50
```
## Troubleshooting
### Issue: "Authentication failed"
**Solution**: Verify Azure AD credentials
- Check `AZURE_TENANT_ID`, `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`
- Verify API permissions are granted with admin consent
- Ensure client secret hasn't expired
### Issue: "Database export failed - not authorized"
**Causes**:
- Only Production environments can be exported
- Trial subscriptions don't support exports
- Missing API permissions
**Solution**: Verify environment is Production with paid subscription
### Issue: "Export timeout exceeded"
**Solution**: Increase timeout
```bash
# In bc-backup.conf
MAX_EXPORT_WAIT_MINUTES="180" # 3 hours
```
### Issue: "Object lock not supported"
**Solution**: Recreate bucket with Object Lock
- Object Lock can only be enabled at bucket creation
- Migrate existing backups to new bucket
### Issue: "Upload failed - access denied"
**Solution**: Check S3 credentials and permissions
```bash
# Test AWS CLI configuration
aws s3 ls --endpoint-url https://s3.amazonaws.com
# Verify bucket policy allows PutObject and PutObjectRetention
```
### Issue: "Decryption failed"
**Solution**: Verify encryption passphrase
- Ensure you're using the correct `ENCRYPTION_PASSPHRASE`
- Check for special characters that might need escaping
## Security Best Practices
1. **Protect Configuration File**
- Set proper permissions: `chmod 600 bc-backup.conf`
- Never commit to version control (use `.gitignore`)
2. **Rotate Credentials Regularly**
- Azure AD client secrets (every 6-12 months)
- S3 access keys (annually)
- Encryption passphrase (when staff changes)
3. **Use Separate Service Account**
- Create dedicated Linux user for backups
- Run with minimal permissions
4. **Encryption Key Management**
- Store `ENCRYPTION_PASSPHRASE` in password manager
- Document in secure runbook
- Test decryption regularly
5. **Monitor for Failures**
- Set up log monitoring/alerting
- Test restore process monthly
6. **Network Security**
- Use HTTPS for S3 endpoints
- Consider VPN for sensitive environments
## Limitations
1. **BC API Limits**
- Maximum 10 database exports per month (Microsoft limit)
- This script tracks recent exports to avoid unnecessary duplicates
2. **Export Restrictions**
- Only Production environments
- Only paid subscriptions
- Exports can take 15-60 minutes
3. **Object Lock Immutability**
- Files cannot be deleted until retention expires
- Ensure adequate S3 storage capacity
- Plan for storage costs
4. **Bandwidth**
- Large databases require significant bandwidth
- Consider S3 transfer costs
## Cost Considerations
### S3 Storage Costs (Example: AWS)
For a 50GB database with hourly backups:
- **Storage**: ~50GB × 720 backups (30 days) = 36TB × $0.023/GB = ~$830/month
- **Uploads**: 720 requests × $0.005/1000 = ~$0.004/month
- **Data Transfer Out** (for restores): $0.09/GB
**Recommendation**: Consider daily backups instead of hourly to reduce costs.
### Optimization Strategies
1. **Reduce Frequency**: Daily or every 6 hours instead of hourly
2. **Lifecycle Policies**: Move older backups to cheaper storage tiers
3. **Incremental Backups**: Consider BC's built-in continuous backup for point-in-time recovery
## Support and Contributing
### Getting Help
1. Check logs: `logs/backup.log`
2. Review troubleshooting section above
3. Check BC Admin Center for export status
4. Verify S3 bucket configuration
### Reporting Issues
When reporting issues, include:
- Relevant log excerpts
- BC environment type (Production/Sandbox)
- S3 provider (AWS/MinIO/etc.)
- Error messages
## License
This backup solution is provided as-is without warranty. Use at your own risk.
## References
- [BC Admin Center API Documentation](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/administration/administration-center-api)
- [BC Data Extraction](https://github.com/microsoft/BCTech/tree/master/samples/ExtractData)
- [AWS S3 Object Lock](https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lock.html)
- [GPG Documentation](https://gnupg.org/documentation/)
## Changelog
### v1.0.0 (2026-01-07)
- Initial release
- Hourly automated backups
- GPG encryption with AES-256
- S3 Object Lock support
- AWS CLI and s3cmd support
- Comprehensive logging