0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
JPPINTO
  • Home
  • Blog
  • Certifications
  • About
  • Contact
  • Shop
  • Gallery
  • Current Setup
Contact

Search

July 5, 2026 / AWS, PowerShell, Windows 10

Transfer S3 Contents Between Buckets with PowerShell

Tags: aws, aws cli, cloud storage, file transfer, powershell, s3
Featured image for Transfer S3 Contents Between Buckets with PowerShell

Moving files between Amazon S3 buckets is usually simple when the same AWS account owns everything. It gets a little more annoying when the source and destination buckets use different credentials, different accounts, or different paths inside the buckets.

This PowerShell script wraps the AWS CLI so you can copy or move objects from one S3 bucket path to another. It can use one AWS profile for the source bucket and another profile for the destination bucket.

The example values in this article are placeholders. Replace the bucket names, paths, regions, and AWS keys with your own values before running the script.

What the Script Does

The Transfer-S3Contents.ps1 script:

  • Builds source and destination S3 URIs from bucket and path settings.
  • Installs AWS CLI v2 if aws is not found.
  • Creates or updates AWS CLI profiles for the source and destination credentials.
  • Tests access to the source S3 path.
  • Tests access to the destination S3 path.
  • Downloads the source objects into a local temp folder.
  • Uploads the temp folder contents to the destination path.
  • Compares source and destination object counts.
  • Leaves the source objects in place when $Mode = "copy".
  • Deletes source objects only when $Mode = "move" and the object counts match.

Copy Mode vs Move Mode

Use copy mode when you want the source bucket to stay unchanged:

$Mode = "copy"

Use move mode only when you want the source objects removed after a successful transfer:

$Mode = "move"

Move mode is intentionally cautious. The script copies the objects, counts the source and destination objects, and only deletes the source when the counts match.

Configure the Buckets and Paths

At the top of the script, set the source and destination bucket names:

$SourceBucket = "source-bucket-name"
$DestinationBucket = "destination-bucket-name"

Use / when you want the root of the bucket:

$SourcePath = "/"
$DestinationPath = "/"

Use folder-style paths when you only want to transfer a specific prefix:

$SourcePath = "/images"
$DestinationPath = "/archive/images"

The script turns those values into S3 URIs such as:

s3://source-bucket-name/images
s3://destination-bucket-name/archive/images

Configure AWS Credentials

The script writes AWS CLI profiles for the source and destination. That lets the source bucket and destination bucket use different credentials when needed.

Set the profile names:

$SourceProfile = "s3-source"
$DestinationProfile = "s3-destination"

Then set the access keys:

$SourceAccessKeyId = "SOURCE-ACCESS-KEY-ID"
$SourceSecretAccessKey = "SOURCE-SECRET-ACCESS-KEY"

$DestinationAccessKeyId = "DESTINATION-ACCESS-KEY-ID"
$DestinationSecretAccessKey = "DESTINATION-SECRET-ACCESS-KEY"

If one AWS key can access both buckets, you can use the same key values for the source and destination settings. If the buckets are in different AWS accounts, use credentials from each account.

The credentials are stored by the AWS CLI under the current Windows user profile:

C:\Users\YourUserName\.aws\credentials
C:\Users\YourUserName\.aws\config

How to Run the Script

Put Transfer-S3Contents.ps1 in a working folder where it can create its local temp directory.

Generic command:

.\script_location\scriptname.ps1

Concrete example:

Set-Location C:\Scripts\S3Transfer
.\Transfer-S3Contents.ps1

You can also ask the script for its built-in help:

.\Transfer-S3Contents.ps1 --help

If PowerShell blocks the script, see:

Make Sure PowerShell Scripts Can Run on Windows

Check the Transfer

After the script finishes, you can inspect both locations with the AWS CLI:

aws s3 ls "s3://source-bucket-name/images" --recursive --profile s3-source
aws s3 ls "s3://destination-bucket-name/archive/images" --recursive --profile s3-destination

For a large transfer, use counts:

aws s3 ls "s3://source-bucket-name/images" --recursive --profile s3-source | Measure-Object
aws s3 ls "s3://destination-bucket-name/archive/images" --recursive --profile s3-destination | Measure-Object

Important Notes

  • Start with $Mode = "copy" until you confirm the transfer works.
  • Use least-privilege AWS keys when possible.
  • Do not publish real AWS access keys in documentation, tickets, screenshots, or chat.
  • Delete or rotate exposed AWS keys immediately if they were ever shared by mistake.
  • The script stores credentials in the normal AWS CLI profile files for the current Windows user.
  • The local temp folder is created beside the script as S3-Move-Temp.
  • For very large buckets, make sure the machine running the script has enough local disk space for the temporary copy.

The full script is below. Save it as Transfer-S3Contents.ps1.

PowerShell Script

Clear-Host
Push-Location $PSScriptRoot
$ProgressPreference = 'SilentlyContinue'

#region Documentation
<#
PURPOSE
    Copy or move objects from one Amazon S3 bucket/path to another Amazon S3 bucket/path.

MODE OPTIONS
    copy
        Copies objects from the source path to the destination path.
        Source objects are NOT deleted.

    move
        Copies objects from the source path to the destination path.
        Confirms the source and destination object counts match.
        Deletes the source objects only after successful verification.

PATH OPTIONS
    Use "/" for the root of the bucket.

    Examples:
        $SourcePath = "/"
        $DestinationPath = "/"

        $SourcePath = "/images"
        $DestinationPath = "/archive/images"

        $SourcePath = "/folder/subfolder"
        $DestinationPath = "/new-folder"

AWS PROFILE NOTES
    AWS CLI uses named profiles to store credentials.

    This script lets you put the Access Key ID and Secret Access Key as variables at the top.
    The script then writes those values into AWS CLI profiles automatically.

    Source profile:
        Used to read/list/delete from the source bucket.

    Destination profile:
        Used to write/list to the destination bucket.

    If the same AWS key has access to both buckets, you can use the same values for:
        $SourceAccessKeyId
        $SourceSecretAccessKey
        $DestinationAccessKeyId
        $DestinationSecretAccessKey

    If the buckets are in different AWS accounts, use the source account key for the source variables
    and the destination account key for the destination variables.

CREDENTIAL FILE LOCATION
    AWS CLI stores credentials here:
        C:\Users\<YourUserName>\.aws\credentials
        C:\Users\<YourUserName>\.aws\config

REFERENCES
    AWS CLI Install:
        https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html

    AWS CLI Configure Files:
        https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html

    AWS S3 Sync:
        https://docs.aws.amazon.com/cli/latest/reference/s3/sync.html

    AWS S3 RM:
        https://docs.aws.amazon.com/cli/latest/reference/s3/rm.html
#>
#endregion

if ($args | Where-Object { $_ -in @("-h", "--help", "-Help", "--usage") }) {
@'
Transfer-S3Contents.ps1

Copy or move objects from one Amazon S3 bucket/path to another Amazon S3 bucket/path.

Usage:
    powershell -ExecutionPolicy Bypass -File .\Transfer-S3Contents.ps1
    powershell -ExecutionPolicy Bypass -File .\Transfer-S3Contents.ps1 --help

Configure these values in the Settings region before running:
    $SourceBucket
    $DestinationBucket
    $SourcePath
    $DestinationPath
    $Mode = "copy" or "move"
    $SourceAccessKeyId / $SourceSecretAccessKey
    $DestinationAccessKeyId / $DestinationSecretAccessKey

Examples:
    $SourcePath = "/"
    $DestinationPath = "/"

    $SourcePath = "/images"
    $DestinationPath = "/archive/images"

    $SourcePath = "/folder/subfolder"
    $DestinationPath = "/new-folder"
'@
    Pop-Location
    exit 0
}

#region Settings
$SourceBucket = "SOURCE-BUCKET-NAME"
$DestinationBucket = "DESTINATION-BUCKET-NAME"

$SourcePath = "/"
$DestinationPath = "/"

$Mode = "copy"

$SourceRegion = "us-east-1"
$DestinationRegion = "us-east-1"

$SourceProfile = "s3-source"
$DestinationProfile = "s3-destination"

$SourceAccessKeyId = "SOURCE-ACCESS-KEY-ID"
$SourceSecretAccessKey = "SOURCE-SECRET-ACCESS-KEY"

$DestinationAccessKeyId = "DESTINATION-ACCESS-KEY-ID"
$DestinationSecretAccessKey = "DESTINATION-SECRET-ACCESS-KEY"

$TempFolder = Join-Path $PSScriptRoot "S3-Move-Temp"
#endregion

#region Functions
function ConvertTo-S3Uri {
    param (
        [Parameter(Mandatory = $true)]
        [string]$Bucket,

        [Parameter(Mandatory = $true)]
        [string]$Path
    )

    $CleanPath = $Path.Trim()

    if ([string]::IsNullOrWhiteSpace($CleanPath) -or $CleanPath -eq "/") {
        return "s3://$Bucket"
    }

    $CleanPath = $CleanPath.TrimStart("/")
    $CleanPath = $CleanPath.TrimEnd("/")

    return "s3://$Bucket/$CleanPath"
}
#endregion

#region Build S3 Paths
Write-Host "Building S3 paths..."

$SourceS3Uri = ConvertTo-S3Uri -Bucket $SourceBucket -Path $SourcePath
$DestinationS3Uri = ConvertTo-S3Uri -Bucket $DestinationBucket -Path $DestinationPath

Write-Host "Source S3 URI: $SourceS3Uri"
Write-Host "Destination S3 URI: $DestinationS3Uri"
Write-Host "Mode: $Mode"
#endregion

#region Check AWS CLI
Write-Host "Checking AWS CLI..."

$AwsCommand = Get-Command aws -ErrorAction SilentlyContinue

if (-not $AwsCommand) {
    Write-Host "AWS CLI not found. Installing AWS CLI v2..."

    $InstallerPath = Join-Path $env:TEMP "AWSCLIV2.msi"

    Invoke-WebRequest `
        -Uri "https://awscli.amazonaws.com/AWSCLIV2.msi" `
        -OutFile $InstallerPath

    if (-not (Test-Path $InstallerPath)) {
        Write-Host "AWS CLI installer download failed."
        exit 1
    }

    Start-Process msiexec.exe `
        -ArgumentList "/i `"$InstallerPath`" /qn" `
        -Wait

    $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")

    $AwsCommand = Get-Command aws -ErrorAction SilentlyContinue

    if (-not $AwsCommand) {
        Write-Host "AWS CLI install completed, but aws.exe was not found in PATH. Open a new PowerShell window and run again."
        exit 1
    }
}

Write-Host "AWS CLI found."
aws --version

if ($LASTEXITCODE -ne 0) {
    Write-Host "AWS CLI version check failed."
    exit 1
}
#endregion

#region Validate Settings
Write-Host "Validating settings..."

if ([string]::IsNullOrWhiteSpace($SourceBucket) -or $SourceBucket -eq "SOURCE-BUCKET-NAME") {
    Write-Host "Source bucket name is not set."
    exit 1
}

if ([string]::IsNullOrWhiteSpace($DestinationBucket) -or $DestinationBucket -eq "DESTINATION-BUCKET-NAME") {
    Write-Host "Destination bucket name is not set."
    exit 1
}

if ($Mode -notin @("copy", "move")) {
    Write-Host "Mode must be copy or move."
    exit 1
}

if ([string]::IsNullOrWhiteSpace($SourceRegion)) {
    Write-Host "Source region is not set."
    exit 1
}

if ([string]::IsNullOrWhiteSpace($DestinationRegion)) {
    Write-Host "Destination region is not set."
    exit 1
}

if ([string]::IsNullOrWhiteSpace($SourceAccessKeyId) -or $SourceAccessKeyId -eq "SOURCE-ACCESS-KEY-ID") {
    Write-Host "Source access key ID is not set."
    exit 1
}

if ([string]::IsNullOrWhiteSpace($SourceSecretAccessKey) -or $SourceSecretAccessKey -eq "SOURCE-SECRET-ACCESS-KEY") {
    Write-Host "Source secret access key is not set."
    exit 1
}

if ([string]::IsNullOrWhiteSpace($DestinationAccessKeyId) -or $DestinationAccessKeyId -eq "DESTINATION-ACCESS-KEY-ID") {
    Write-Host "Destination access key ID is not set."
    exit 1
}

if ([string]::IsNullOrWhiteSpace($DestinationSecretAccessKey) -or $DestinationSecretAccessKey -eq "DESTINATION-SECRET-ACCESS-KEY") {
    Write-Host "Destination secret access key is not set."
    exit 1
}

Write-Host "Settings validated."
#endregion

#region Configure Source Credentials
Write-Host "Configuring source AWS CLI profile..."

aws configure set aws_access_key_id $SourceAccessKeyId --profile $SourceProfile

if ($LASTEXITCODE -ne 0) {
    Write-Host "Failed to set source access key."
    exit 1
}

aws configure set aws_secret_access_key $SourceSecretAccessKey --profile $SourceProfile

if ($LASTEXITCODE -ne 0) {
    Write-Host "Failed to set source secret key."
    exit 1
}

aws configure set region $SourceRegion --profile $SourceProfile

if ($LASTEXITCODE -ne 0) {
    Write-Host "Failed to set source region."
    exit 1
}

Write-Host "Source AWS CLI profile configured: $SourceProfile"
#endregion

#region Configure Destination Credentials
Write-Host "Configuring destination AWS CLI profile..."

aws configure set aws_access_key_id $DestinationAccessKeyId --profile $DestinationProfile

if ($LASTEXITCODE -ne 0) {
    Write-Host "Failed to set destination access key."
    exit 1
}

aws configure set aws_secret_access_key $DestinationSecretAccessKey --profile $DestinationProfile

if ($LASTEXITCODE -ne 0) {
    Write-Host "Failed to set destination secret key."
    exit 1
}

aws configure set region $DestinationRegion --profile $DestinationProfile

if ($LASTEXITCODE -ne 0) {
    Write-Host "Failed to set destination region."
    exit 1
}

Write-Host "Destination AWS CLI profile configured: $DestinationProfile"
#endregion

#region Create Temp Folder
Write-Host "Creating temp folder..."

if (-not (Test-Path $TempFolder)) {
    New-Item -ItemType Directory -Path $TempFolder | Out-Null
}

if (-not (Test-Path $TempFolder)) {
    Write-Host "Failed to create temp folder."
    exit 1
}

Write-Host "Temp folder ready: $TempFolder"
#endregion

#region Test Source Path Access
Write-Host "Testing source path access..."

aws s3 ls "$SourceS3Uri" --profile $SourceProfile --region $SourceRegion

if ($LASTEXITCODE -ne 0) {
    Write-Host "Could not access source path."
    exit 1
}

Write-Host "Source path access confirmed."
#endregion

#region Test Destination Path Access
Write-Host "Testing destination path access..."

aws s3 ls "$DestinationS3Uri" --profile $DestinationProfile --region $DestinationRegion

if ($LASTEXITCODE -ne 0) {
    Write-Host "Could not access destination path."
    exit 1
}

Write-Host "Destination path access confirmed."
#endregion

#region Download From Source
Write-Host "Downloading objects from source path..."

aws s3 sync "$SourceS3Uri" "$TempFolder" --profile $SourceProfile --region $SourceRegion

if ($LASTEXITCODE -ne 0) {
    Write-Host "Download from source path failed."
    exit 1
}

Write-Host "Download completed."
#endregion

#region Upload To Destination
Write-Host "Uploading objects to destination path..."

aws s3 sync "$TempFolder" "$DestinationS3Uri" --profile $DestinationProfile --region $DestinationRegion

if ($LASTEXITCODE -ne 0) {
    Write-Host "Upload to destination path failed."
    exit 1
}

Write-Host "Upload completed."
#endregion

#region Verify Object Counts
Write-Host "Checking source object count..."

$SourceCount = aws s3 ls "$SourceS3Uri" --recursive --profile $SourceProfile --region $SourceRegion | Measure-Object | Select-Object -ExpandProperty Count

if ($LASTEXITCODE -ne 0) {
    Write-Host "Failed to count source path objects."
    exit 1
}

Write-Host "Checking destination object count..."

$DestinationCount = aws s3 ls "$DestinationS3Uri" --recursive --profile $DestinationProfile --region $DestinationRegion | Measure-Object | Select-Object -ExpandProperty Count

if ($LASTEXITCODE -ne 0) {
    Write-Host "Failed to count destination path objects."
    exit 1
}

Write-Host "Source object count: $SourceCount"
Write-Host "Destination object count: $DestinationCount"

if ($SourceCount -ne $DestinationCount) {
    Write-Host "Object counts do not match. Source will not be deleted."
    exit 1
}

Write-Host "Object counts match."
#endregion

#region Delete Source If Move Mode
if ($Mode -eq "move") {
    Write-Host "Move mode selected."
    Write-Host "Source objects will now be deleted because copy and verification succeeded."

    aws s3 rm "$SourceS3Uri" --recursive --profile $SourceProfile --region $SourceRegion

    if ($LASTEXITCODE -ne 0) {
        Write-Host "Source delete failed."
        exit 1
    }

    Write-Host "Source objects deleted."
}
else {
    Write-Host "Copy mode selected. Source objects were not deleted."
}
#endregion

#region Complete
Write-Host "S3 $Mode completed successfully."
Write-Host "Credentials saved under:"
Write-Host "$env:USERPROFILE\.aws\credentials"
Write-Host "$env:USERPROFILE\.aws\config"
$ProgressPreference = 'Continue'
#endregion

References

  • AWS CLI install guide
  • AWS CLI configuration files
  • AWS CLI %%CODE0%%
  • AWS CLI %%CODE0%%
Post Views: 14
<- Configure UFW Firewall on Ubuntu for Web Servers
Entering .com in Bucket Names Causes SSL Errors ->

Categories

  • Active Directory (5)
  • AI (2)
  • Amazon Cloud Services (1)
  • AWS (2)
  • Blazor (1)
  • C# (C-Sharp) (3)
  • CI/CD Pipelines (1)
  • Cloud (1)
  • Containers (4)
  • Deployment (2)
  • Development (4)
  • Docker (3)
  • General (5)
  • IIS 6.0 (4)
  • IIS 7.0 (10)
  • IIS 8.0 (1)
  • Infrastructure as Code (IaC) (1)
  • Kubernetes (3)
  • Linux (9)
  • Microsoft 365 (2)
  • MySQL (1)
  • Office 2010 (1)
  • PHP (1)
  • PowerShell (8)
  • Productivity (1)
  • Servers (9)
  • SharePoint 2007 (8)
  • SharePoint 2010 (19)
  • SharePoint 2013 (2)
  • SharePoint Online (1)
  • SMTP (4)
  • SQL Server 2008 (1)
  • SQL Server 2008 R2 (1)
  • SQL Server 2012 (2)
  • SQL Server 2019 (1)
  • Troubleshooting (1)
  • Ubuntu (9)
  • Uncategorized (1)
  • URL Rewrite (2)
  • Visual Studio 2019 (1)
  • Visual Studio Code (1)
  • Windows 10 (6)
  • Windows 2003 (9)
  • Windows 2008 (18)
  • Windows 2012 (6)
  • Windows 7 (3)
  • Windows Firewall (1)
  • Windows Vista (1)
  • WordPress (3)
  • WP-CLI (3)

Recent Posts

  • Entering .com in Bucket Names Causes SSL Errors
  • Transfer S3 Contents Between Buckets with PowerShell
  • Configure UFW Firewall on Ubuntu for Web Servers
  • Create an Nginx Default Catch-All Site on Ubuntu
  • Install and Configure Redis on Ubuntu for Local Object Cache

Advertisement

Tags

ai coding agents aws bash cloud storage developer workflow dev to production externalize blob externalize sharepoint data filezilla server firewall rules filazilla full installation http redirect https IIS iis7 iis 7 installation IIS installation index server configuration installing cumulative updates linux load balance central administration microsoft 365 nginx powerpoint powershell redirect http to https s3 server setup sharepoint 2010 cumulative updates sharepoint 2010 farm build sharepoint 2010 farm configuration sharepoint 2010 farm installation sharepoint data externalization SMTP ssl storagepoint ubuntu web server windows Windows 7 windows firewall configuration windows server 2008 wordpress wp-cli x86
© 2026 JPPinto.com. All rights reserved.