Uploading to S3 in Bash[source]
xml
<glacius:metadata> | |
<title>Uploading to S3 in Bash</title> | |
<description>How to upload a file to S3 using Bash circa 2014</description> | |
<category>Legacy blog posts</category> | |
<category>Programming</category> | |
<category>AWS</category> | |
<category>Bash</category> | |
</glacius:metadata> | |
<glacius:macro name="legacy blargh banner"> | |
<properties> | |
<originalUrl>https://tmont.com/blargh/2014/1/uploading-to-s3-in-bash</originalUrl> | |
<originalDate>2014-01-04T05:01:37.000Z</originalDate> | |
</properties> | |
</glacius:macro> | |
<p> | |
There are already a couple of ways | |
to do this using a <a href="https://github.com/cosmin/s3-bash">3rd party library</a>, | |
but I didn't really feel like including and sourcing several hundred lines of | |
code just to run a CURL command. So here's how you can upload a file to S3 using | |
the <a href="https://docs.aws.amazon.com/AmazonS3/latest/API/Welcome.html">REST API</a>. | |
</p> | |
<p> | |
This example uploads a gzipped tarball; you'll need to adjust the | |
content-type accordingly. And obviously use a real API key and secret. | |
</p> | |
<glacius:code lang="bash"><![CDATA[ | |
file=/path/to/file/to/upload.tar.gz | |
bucket=your-bucket | |
resource="/${bucket}/${file}" | |
contentType="application/x-compressed-tar" | |
dateValue=`date -R` | |
stringToSign="PUT\n\n${contentType}\n${dateValue}\n${resource}" | |
s3Key=xxxxxxxxxxxxxxxxxxxx | |
s3Secret=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | |
signature=`echo -en ${stringToSign} | openssl sha1 -hmac ${s3Secret} -binary | base64` | |
curl -X PUT -T "${file}" \ | |
-H "Host: ${bucket}.s3.amazonaws.com" \ | |
-H "Date: ${dateValue}" \ | |
-H "Content-Type: ${contentType}" \ | |
-H "Authorization: AWS ${s3Key}:${signature}" \ | |
https://${bucket}.s3.amazonaws.com/${file} | |
]]></glacius:code> | |
<p> | |
As someone who isn't abundantly talented at writing shell scripts, the | |
tricky part was finding the <code>-e</code> option for <code>echo</code>, | |
which makes it handle character escapes (e.g. <code>\n</code>). It's | |
kind of annoyingly complex to actually have a newline character in a | |
string in bash. | |
</p> | |
<p> | |
Anyway, this little snippet is suitable for running as a cron job or | |
just a one-off from the shell. Note that if you want to add other | |
<a href="https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html#API_PutObject_RequestSyntax">amazon-specific headers</a> | |
(such as setting permissions) you'll need to | |
manually add those to <code>stringToSign</code> since they need to | |
be part of the authorization signature. | |
</p> | |
<h3>Backup Script</h3> | |
<p> | |
The reason I needed to figure this out was that I wanted to run a backup | |
script that uploaded stuff to an S3 bucket. I run this in a cron job once | |
a week. It backs up a Git server, a MySQL database and some nginx configuration | |
files. It's just a real-world example of how to upload to S3 from the shell. | |
</p> | |
<glacius:code lang="bash"><![CDATA[ | |
#!/bin/bash | |
cd /tmp | |
rm -rf backup | |
mkdir backup | |
cd backup | |
mkdir sql && cd sql | |
databases=`echo 'show databases;' | mysql -u backup | tail -n +2 | grep -v _schema | grep -v mysql` | |
for database in $databases | |
do | |
mysqldump -u backup --databases $database > "${database}.sql" | |
done | |
cd .. | |
mkdir nginx && cd nginx | |
cp -R /etc/nginx/sites-enabled . | |
cp /etc/nginx/nginx.conf . | |
cd .. | |
mkdir git && cd git | |
repos=`ls -1 /home/git | grep '.git$'` | |
for repo in $repos; do | |
cp -R "/home/git/${repo}" . | |
done | |
cd .. | |
date=`date +%Y%m%d` | |
bucket=my-bucket | |
for dir in git nginx sql; do | |
file="${date}-${dir}.tar.gz" | |
cd $dir && tar czf $file * | |
resource="/${bucket}/${file}" | |
contentType="application/x-compressed-tar" | |
dateValue=`date -R` | |
stringToSign="PUT\n\n${contentType}\n${dateValue}\n${resource}" | |
s3Key=xxxxxxxxxxxxxxxxxxxx | |
s3Secret=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | |
signature=`echo -en ${stringToSign} | openssl sha1 -hmac ${s3Secret} -binary | base64` | |
curl -X PUT -T "${file}" \ | |
-H "Host: ${bucket}.s3.amazonaws.com" \ | |
-H "Date: ${dateValue}" \ | |
-H "Content-Type: ${contentType}" \ | |
-H "Authorization: AWS ${s3Key}:${signature}" \ | |
https://${bucket}.s3.amazonaws.com/${file} | |
cd .. | |
done | |
cd | |
rm -rf /tmp/backup | |
]]></glacius:code> | |