> For the complete documentation index, see [llms.txt](https://cookbook.geuer-pollmann.de/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://cookbook.geuer-pollmann.de/azure/upload-from-bash.md).

# 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](https://docs.microsoft.com/en-us/rest/api/storageservices/understanding-block-blobs--append-blobs--and-page-blobs#about-block-blobs) 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.

```bash
#!/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}" )"
```
