Backup safety is one of the main pillars of database administration. Keeping an external copy guarantees data recovery in case of failure. In this article, we will show how to configure a SQL Server backup using Rclone to send the files to an Oracle Cloud bucket.

Creating the bucket in Oracle Cloud

  1. Log in to Oracle Cloud: https://cloud.oracle.com
  2. Go to the Storage section and create a Bucket

3. Save the generated namespace, as it will be needed for the configuration

Generating the access keys

  1. Log in to Oracle Cloud and create a Secret Key and Access Key

Write down the Secret Key at generation time, as it will not be shown again

Installing and configuring Rclone on Windows

1. Download Rclone

Download the latest Rclone version for Windows: https://rclone.org/downloads/

2. Initial Rclone configuration

  1. Open the Command Prompt (CMD)
  2. Type rclone.exe config and follow the steps below:
    • Choose n to create a new configuration
    • Name the configuration oci_s3
    • Choose s3 as the storage type
    • Select Other as the provider
    • Choose 1 to enter the credentials manually
    • Enter your Access Key and Secret Key
    • Provide the region (example: sa-saopaulo-1)
    • Set the endpoint in the format: https://NAMESPACE.compat.objectstorage.REGION.oraclecloud.com
    • The rest of the configuration can stay at the defaults, just press enter.

3. Test your configuration

Creating the PowerShell script for automatic backup

Now that Rclone is configured, we can create a PowerShell script to automate the backup and the upload to Oracle Cloud.

PowerShell script example:

This script takes as input:

[Console]::OutputEncoding = [System.Text.Encoding]::UTF8

# Diretório de backup
$backupFolder = "K:\BACKUPS\LOG"
$RcloneConfig = "C:\zabbix\scripts\rclone.conf"

# Nome do bucket na OCI
$bucketName = "BACKUP-MSSQL-OCI/LOG"

# Nome do remote configurado no Rclone
$remote = "oci_s3"

# Diretório de logs
$logDir = "C:\zabbix\scripts\logs"

# Criar diretório de logs se não existir
if (!(Test-Path -Path $logDir)) {
    New-Item -ItemType Directory -Path $logDir | Out-Null
}

# Criar nome do log por dia
$logFile = "$logDir\backup_LOG_$(Get-Date -Format 'yyyyMMdd_HHmm').log"

# Data e hora atual
$date = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Write-Output "[$date] Iniciando verificação e sincronização..." | Out-File -Append -Encoding utf8 $logFile

# Função para verificar se um arquivo está em uso
function Test-FileInUse {
    param([string]$filePath)
    try {
        $file = [System.IO.File]::Open($filePath, 'Open', 'Read', 'None')
        if ($file) { $file.Close() }
        return $false  # Arquivo não está em uso
    } catch {
        return $true   # Arquivo está em uso
    }
}

# Definir os diretórios do dia atual e do dia anterior
$today = Get-Date -Format 'yyyyMMdd'
$yesterday = (Get-Date).AddDays(-1).ToString('yyyyMMdd')
$todayDir = Join-Path $backupFolder $today
$yesterdayDir = Join-Path $backupFolder $yesterday

# Lista de diretórios válidos
$validDirs = @()
if (Test-Path $todayDir) { $validDirs += $todayDir }
if (Test-Path $yesterdayDir) { $validDirs += $yesterdayDir }

# Criar uma lista de arquivos prontos para upload
$filesToSync = @()
foreach ($dir in $validDirs) {
    Get-ChildItem -Path $dir -Recurse -File | ForEach-Object {
        $filePath = $_.FullName
        if (-not (Test-FileInUse $filePath)) {
            $filesToSync += $filePath
        } else {
            Write-Output "[$date] Ignorando (arquivo ainda em uso): $filePath" | Out-File -Append -Encoding utf8 $logFile
        }
    }
}

# Se houver arquivos prontos, enviar para o OCI
if ($filesToSync.Count -gt 0) {
    Write-Output "[$date] Enviando $( $filesToSync.Count ) arquivos para OCI..." | Out-File -Append -Encoding utf8 $logFile

    # Criar diretório no bucket
    $bucketPathToday = "$remote`:$bucketName/$today"
    $bucketPathYesterday = "$remote`:$bucketName/$yesterday"

    # Enviar arquivos com Rclone
    if (Test-Path $todayDir) {
        & "C:\Program Files\Rclone\rclone.exe" copy "$todayDir" "$bucketPathToday" --config "$RcloneConfig" --progress --transfers 8 --checkers 16 --fast-list --log-file="$logFile" --log-level DEBUG
    }
    if (Test-Path $yesterdayDir) {
        & "C:\Program Files\Rclone\rclone.exe" copy "$yesterdayDir" "$bucketPathYesterday" --config "$RcloneConfig" --progress --transfers 8 --checkers 16 --fast-list --log-file="$logFile" --log-level DEBUG
    }

    Write-Output "[$date] Sincronização concluída!" | Out-File -Append -Encoding utf8 $logFile
} else {
    Write-Output "[$date] Nenhum arquivo disponível para sincronização." | Out-File -Append -Encoding utf8 $logFile
}

Scheduling the backup in SQL Server Agent

  1. Open SQL Server Management Studio (SSMS)
  2. Go to SQL Server Agent > Jobs
  3. Open the backup job that needs the upload step.
  4. Choose CmdExec as the command type and insert the script above (adjusted to your environment).
  5. Insert the powershell script call C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -File "C:\scripts\furushima_send_oci_log.ps1"
  6. Set an appropriate schedule (daily, hourly, etc.)

And there we go…. our backups are uploaded to OCI S3, automatically right after the backup! 🙂