Upload to Amazon S3 from Powershell v2

Powershell v2 lacks many of the commandlets that we have come to know and love on more modern versions. Every now and then, you just have to do something on Windows 7 and you can’t update Powershell.

Below is an example script to PUT a file to an Amazon S3 bucket using Powershell v2. The missing functionality is the ability to set the “Date” HTTP header when using [System.Net.HttpWebRequest]. Even though the docs state that it should be settable, it is not. The Date header is required for AWS to validate the client generated signature.

Luckily, Amazon provides the x-amz-date header which can be used instead of Date. In the below example, $output contains the data that will be added to the uploaded file.

# S3 configuration
$s3Bucket = "name-of-s3-bucket-here"
$s3AccessKey="account-access-key-here"
$s3SecretKey="account-secret-key-here"
$fileName = "name-of-the-file-that-will-be-uploaded"
$relativePath="/$s3Bucket/$fileName"
$s3URL="http://$s3Bucket.s3.amazonaws.com/$fileName"

# build the S3 request
$dateFormatted = get-date -UFormat "%a, %d %b %Y %T %Z00"
$httpMethod="PUT"
$contentType="application/octet-stream"
$stringToSign="$httpMethod`n`n$contentType`n`nx-amz-date:$dateFormatted`n$relativePath"

# generate the signature
$hmacsha = New-Object System.Security.Cryptography.HMACSHA1
$hmacsha.key = [Text.Encoding]::ASCII.GetBytes($s3SecretKey)
$signature = $hmacsha.ComputeHash([Text.Encoding]::ASCII.GetBytes($stringToSign))
$signature = [Convert]::ToBase64String($signature)

# Convert the message body to a byte array
$BodyBytes = [System.Text.Encoding]::UTF8.GetBytes($stringToSign.tostring());
# Set the URI of the web service
$URI = [System.Uri]$s3URL;

# Create a new web request
$WebRequest = [System.Net.HttpWebRequest]::Create($URI);
$WebRequest.Method = $httpMethod;
$WebRequest.ContentType = $contentType
$WebRequest.PreAuthenticate = $true;
$WebRequest.Accept = "*/*" # may not be required
$WebRequest.UserAgent = "powershell" # not required

# this is needed to generate a valid signature!
$WebRequest.Headers.add('x-amz-date', $dateFormatted)
$WebRequest.Headers["Authorization"] = "AWS $s3AccessKey`:$signature";

# Write the message body to the request stream, close, and get response
$WebStream = $WebRequest.GetRequestStream()
$WebStream.Write($BodyBytes, 0, $BodyBytes.Length);
$WebStream.flush()
$WebStream.close()
$res = $WebRequest.getResponse()

2 thoughts on “Upload to Amazon S3 from Powershell v2”

  1. You’re awesome for posting this!
    Just a small correction, you are using the variable $output in the call to UTF8.GetBytes() on line 22, whereas it should be $stringToSign.
    Otherwise everything works as expected. Great job!

Leave a Reply

Your email address will not be published. Required fields are marked *