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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://cookbook.geuer-pollmann.de/azure/upload-from-bash.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
