Upload a file from bash

This short demo shows how one can upload a file into Azure storage, using just basic utilities (i.e. no Microsoft-provided tooling).

Requirements

To do so, we need a few requirements satisfied:

  • bash, curl and jq

    • The cURL utility must be locally installed

    • The jq utility (https://stedolan.github.io/jq/) must be locally installed

  • You need to have an Azure storage account

    • In that storage account, there needs to be a storage container already created

    • You need to have the Storage Blob Data Contributor role assigned

  • Obviously you need some local file

    • Given that we'll upload the file in a single shot, it must be less than 5000 MiB in size

How it works

  1. The script attempts a device code signin to the given Azure AD tenant, requesting a token for Azure storage

  2. Once the script outputs the device code, you need to open https://microsoft.com/devicecode in a web browser and punch in the device code

  3. The script locally computes the MD5 hash of the file (using md5sum, awk, xxd and base64), to ensure upload errors will be catched

  4. The script uploads the file to blob storage.

#!/bin/bash

aadTenant="chgeuerfte.onmicrosoft.com"
storageAccountName="chgeuer"
containerName="public"
filename="/mnt/c/Users/chgeuer/Desktop/dump.md"

resource="https://storage.azure.com/.default"
az_cli_client_id="04b07795-8ddb-461a-bbee-02f9e1bf7b46"
client_id="${az_cli_client_id}"

deviceResponse="$( curl \
    --silent \
    --request POST \
    --url "https://login.microsoftonline.com/${aadTenant}/oauth2/v2.0/devicecode"\
    --data-urlencode "client_id=${client_id}" \
    --data-urlencode "scope=${resource}" \
    )"

device_code="$(echo "${deviceResponse}" | jq -r ".device_code")"
sleep_duration="$(echo "${deviceResponse}" | jq -r ".interval")"
access_token=""

while [[ "${access_token}" == "" ]]
do
    tokenResponse="$( curl \
        --silent \
        --request POST \
        --url "https://login.microsoftonline.com/{aadTenant}/oauth2/v2.0/token" \
        --data-urlencode "client_id=${client_id}" \
        --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
        --data-urlencode "device_code=${device_code}" \
        )"

    if [[ "$( echo "${tokenResponse}" | jq -r ".error" )" == "authorization_pending" ]]; then
      echo "$( echo "${deviceResponse}" | jq -r ".message" )"
      sleep "${sleep_duration}"
    else
      access_token="$( echo "${tokenResponse}" | jq -r ".access_token" )"
      echo "User authenticated"
    fi
done

curl \
    --request PUT \
    --url "https://${storageAccountName}.blob.core.windows.net/${containerName}/$( basename "${filename}" )" \
    --header "x-ms-version: 2019-12-12" \
    --header "x-ms-blob-type: BlockBlob"\
    --header "x-ms-blob-content-disposition: attachment; filename=\"$( basename "${filename}" )\"" \
    --header "Content-Type: application/binary" \
    --header "Authorization: Bearer ${access_token}" \
    --header "Content-MD5: $( md5sum "${filename}" | awk '{ print $1 }' | xxd -r -p | base64 )" \
    --upload-file "${filename}"
    
echo "File uploaded to https://${storageAccountName}.blob.core.windows.net/${containerName}/$( basename "${filename}" )"

Last updated