Automatiza con Azure CLI: transfiere archivos y verifica bytes
Usa az storage blob upload para enviar un archivo local a Azure Blob Storage; luego descárgalo y
compara los bytes. El siguiente script hace esto con un archivo o un directorio de archivos. Rechaza
los nombres de blob existentes y solo informa que un lote está completo cuando todos los archivos
seleccionados pasan la comparación.
Instalación y configuración
Este tutorial usa Bash en Linux y el contenedor de Azure CLI de Microsoft, probado con Azure CLI 2.91.0. Necesitas una instalación de Docker funcional, permiso para ejecutar contenedores y un directorio de trabajo con permisos de escritura. Mantén los archivos de origen sin cambios durante la transferencia. Las transferencias y el almacenamiento generan cargos de Azure; empieza con archivos pequeños que no contengan información confidencial.
El contenedor incluye Bash y Python, por lo que el script no necesita que Azure CLI esté instalado en el equipo anfitrión. Sigue la guía de contenedores de Microsoft para conocer los detalles de instalación de la imagen. Descarga la imagen probada y comprueba la CLI dentro de ella:
docker pull mcr.microsoft.com/azure-cli:2.91.0-azurelinux3.0 &&
docker run --rm mcr.microsoft.com/azure-cli:2.91.0-azurelinux3.0 az version
Elige el destino y la autorización
Usa una cuenta de almacenamiento y un contenedor de blobs privado existentes en los que tu administrador te haya autorizado a escribir. Para las cuentas nuevas, usa el tipo de uso general v2, como se describe en la descripción general de las cuentas de Microsoft. Aquí, privado significa que el acceso anónimo a los blobs está deshabilitado. Tu equipo sigue necesitando una ruta de red funcional al endpoint de Blob de la cuenta.
Este ejemplo usa autenticación con Shared Key. Tu administrador debe permitirla explícitamente y
proporcionar el nombre y la clave de la cuenta mediante tu gestor de secretos como las variables de
entorno exportadas AZURE_STORAGE_ACCOUNT y AZURE_STORAGE_KEY. La clave otorga un
acceso amplio a la cuenta: mantenla fuera de los scripts, del historial de la shell y de los
registros. Ejecuta esto solo donde esa autorización sea apropiada.
Microsoft recomienda la autorización con Microsoft Entra
cuando esté disponible. Con --auth-mode login, la identidad necesita un rol de datos de
blobs; un rol de administración del almacenamiento por sí solo no es suficiente. Este script exige
deliberadamente que se proporcione una clave; no inicia sesión, busca claves de cuenta, crea recursos
ni asigna roles. Una cuenta que deshabilite Shared Key necesita otra configuración de autenticación.
Transfiere archivos con Azure CLI
Guarda lo siguiente como transfer.sh en tu directorio de trabajo. Sus cuatro
argumentos son el archivo o directorio de origen, el nombre del contenedor existente, el nuevo
prefijo de los nombres de blob y el nuevo directorio de descarga. Usa rutas relativas dentro de ese
directorio de trabajo, como ./input.bin y ./downloads, para que el
contenedor pueda acceder a ellas. El directorio padre del directorio de descarga ya debe existir.
Las rutas de origen y los nombres de archivo deben ser válidos en UTF-8 y no contener retornos de
carro ni saltos de línea. Se admiten espacios, signos de porcentaje literales,
#, ?, guiones iniciales y nombres de archivo
Unicode. El prefijo usa letras ASCII minúsculas, dígitos, barras, guiones bajos y guiones; haz que
empiece con una letra o un dígito y evita los segmentos vacíos, . o
... Un prefijo como transfers/run-001 crea nombres de blob
anidados sin necesidad de crear directorios remotos.
#!/usr/bin/env bash
set -uo pipefail
for tool in az python3 mkdir sleep; do
command -v "$tool" >/dev/null || { printf 'Missing tool: %s\n' "$tool" >&2; exit 1; }
done
: "${AZURE_STORAGE_ACCOUNT:?Set AZURE_STORAGE_ACCOUNT}"
: "${AZURE_STORAGE_KEY:?Set AZURE_STORAGE_KEY}"
unset AZURE_STORAGE_CONNECTION_STRING AZURE_STORAGE_SAS_TOKEN AZURE_STORAGE_SERVICE_ENDPOINT
if [ "$#" -ne 4 ]; then
printf 'Usage: bash transfer.sh SOURCE CONTAINER PREFIX DOWNLOAD_DIR\n' >&2
exit 2
fi
source_path=$1
container=$2
prefix=$3
download_dir=$4
case "$source_path" in /*) ;; *) source_path="./$source_path" ;; esac
case "$download_dir" in /*) ;; *) download_dir="./$download_dir" ;; esac
if ! python3 - "$source_path" "$prefix" "$download_dir" <<'PY'
import os
import re
import sys
try:
source, prefix, destination = sys.argv[1:]
paths = [source, destination]
if os.path.isdir(source):
with os.scandir(source) as entries:
paths.extend(entry.path for entry in entries
if entry.is_file(follow_symlinks=False))
for path in paths:
os.fsencode(path).decode('utf-8')
if '\n' in path or '\r' in path:
raise ValueError()
if not re.fullmatch(r'[a-z0-9][a-z0-9/_-]*', prefix):
raise ValueError()
if any(part in ('', '.', '..') for part in prefix.split('/')):
raise ValueError()
except (OSError, UnicodeError, ValueError):
print('Check source access, UTF-8 paths, and the blob prefix.', file=sys.stderr)
sys.exit(1)
PY
then
exit 1
fi
if [ -L "$source_path" ] || { [ ! -f "$source_path" ] && [ ! -d "$source_path" ]; }; then
printf 'Source must be a regular file or directory, not a symbolic link.\n' >&2
exit 1
fi
mkdir -- "$download_dir" || exit 1
shopt -s nullglob dotglob
files=("$source_path")
if [ -d "$source_path" ]; then files=("$source_path"/*); fi
failed=0
verified=0
max_retries=3
for file in "${files[@]}"; do
[ -f "$file" ] && [ ! -L "$file" ] || continue
filename=${file##*/}
blob_name="$prefix/$filename"
uploaded=0
for ((attempt=1; attempt<=max_retries; attempt++)); do
if az storage blob upload --account-name "$AZURE_STORAGE_ACCOUNT" \
--container-name "$container" --name "$blob_name" --file "$file" \
--type block --auth-mode key --overwrite false \
--only-show-errors --output none >/dev/null 2>&1; then
uploaded=1
break
fi
if [ "$attempt" -lt "$max_retries" ]; then sleep 5; fi
done
if [ "$uploaded" -eq 0 ]; then
printf 'Upload refused or failed: %s\n' "$filename" >&2
failed=$((failed + 1))
continue
fi
downloaded_file="$download_dir/$filename"
if ! az storage blob download --account-name "$AZURE_STORAGE_ACCOUNT" \
--container-name "$container" --name "$blob_name" --file "$downloaded_file" \
--auth-mode key --overwrite false --only-show-errors --output none >/dev/null 2>&1; then
printf 'Download failed: %s\n' "$filename" >&2
failed=$((failed + 1))
continue
fi
if ! python3 - "$file" "$downloaded_file" <<'PY'
import filecmp
import sys
sys.exit(0 if filecmp.cmp(sys.argv[1], sys.argv[2], shallow=False) else 1)
PY
then
printf 'Byte comparison failed: %s\n' "$filename" >&2
failed=$((failed + 1))
continue
fi
printf 'Verified: %s\n' "$filename"
verified=$((verified + 1))
done
printf 'Verified %s file(s); failed %s.\n' "$verified" "$failed"
if [ "$failed" -ne 0 ]; then exit 1; fi
printf 'Transfer complete\n'
Sube archivos
Coloca un archivo llamado input.bin junto al script guardado. Con las dos
variables de credenciales ya exportadas, reemplaza your-container por el nombre de tu
contenedor existente y elige un prefijo nuevo. Ejecuta:
docker run --rm --user "$(id -u):$(id -g)" \
--env HOME=/tmp --env AZURE_CONFIG_DIR=/tmp/azure \
--env AZURE_CORE_COLLECT_TELEMETRY=no \
--env AZURE_STORAGE_ACCOUNT --env AZURE_STORAGE_KEY \
--mount "type=bind,source=$PWD,target=/work" --workdir /work \
mcr.microsoft.com/azure-cli:2.91.0-azurelinux3.0 \
bash transfer.sh ./input.bin your-container transfers/run-001 ./downloads
El contenedor termina después; el blob subido permanece en Azure y la descarga permanece en tu
directorio de trabajo. La opción --user mantiene a tu usuario de Linux como
propietario de los archivos descargados.
Descarga archivos
El comando az storage blob download del script recupera el nombre exacto del blob que subió, y
Python compara la descarga completa con el archivo de origen local. Una ejecución correcta con un
solo archivo imprime Verified: input.bin, luego Verified 1 file(s); failed 0. y
Transfer complete. Los archivos vacíos también son válidos. Una subida correcta seguida
de una descarga o comparación fallida devuelve un estado distinto de cero y deja el blob remoto en
su lugar para inspeccionarlo.
Ambas transferencias usan explícitamente --overwrite false; consulta la
referencia de subida
y la referencia de descarga.
El script también rechaza un directorio de descarga existente antes de contactar con Azure. Para
repetir una ejecución, elige un prefijo nuevo y un directorio de descarga nuevo. Reutilizar un nombre
de blob con un directorio de descarga nuevo provoca un fallo en lugar de reemplazar el blob anterior.
Ese rechazo es intencional; el script no implementa la sustitución en el mismo lugar ni la reversión.
Automatiza transferencias de archivos por lotes con Azure CLI
Coloca tus archivos en ./input y luego usa el mismo script con ese directorio
como primer argumento:
docker run --rm --user "$(id -u):$(id -g)" \
--env HOME=/tmp --env AZURE_CONFIG_DIR=/tmp/azure \
--env AZURE_CORE_COLLECT_TELEMETRY=no \
--env AZURE_STORAGE_ACCOUNT --env AZURE_STORAGE_KEY \
--mount "type=bind,source=$PWD,target=/work" --workdir /work \
mcr.microsoft.com/azure-cli:2.91.0-azurelinux3.0 \
bash transfer.sh ./input your-container transfers/batch-001 ./batch-downloads
Esto selecciona los archivos regulares que están directamente dentro de
./input, incluidos los archivos ocultos. Omite los subdirectorios y los enlaces
simbólicos; no recorre los directorios de forma recursiva. Cada nombre de archivo seleccionado se
convierte en el último segmento del nombre de su blob y en el nombre de su descarga local. Un
directorio vacío finaliza con cero archivos verificados, así que comprueba el recuento impreso si
esperabas un inventario mayor.
Las transferencias se ejecutan secuencialmente. Cada subida recibe como máximo tres invocaciones de
la CLI, separadas por cinco segundos después de un fallo; los reintentos de servicio de la propia CLI
son adicionales. Las descargas reciben una invocación de la CLI. El recuento final incluye todas las
subidas, descargas y comparaciones fallidas, aunque los archivos posteriores se procesen
correctamente. Un lote parcialmente correcto termina con un estado distinto de cero y conserva los
blobs y las descargas que se completaron correctamente. Nunca imprime Transfer complete
para ese resultado.
Soluciona problemas comunes
Si falta una variable de credenciales, el script se detiene antes de crear el directorio de descarga. Si se rechaza una subida, comprueba con el administrador la cuenta seleccionada, el contenedor, la autorización de la clave y la ruta de red. El script informa del nombre del archivo afectado y omite los diagnósticos sin procesar de la CLI para mantener las credenciales y las URL firmadas fuera de su salida.
Un fallo de subida también puede significar que el nombre del blob ya existe. Tres intentos rechazados no hacen que la sustitución sea segura. Una respuesta perdida puede dejar un blob confirmado aunque el comando haya fallado; inspecciona ese nombre exacto antes de volver a intentarlo. Usa un prefijo nuevo para otra copia en lugar de cambiar la política de sobrescritura para eludir un rechazo.
Si falla la descarga o la comparación, inspecciona por separado el blob conservado y la descarga local. Mantén intacto el archivo de origen. El script no elimina las descargas parciales, deshace las subidas correctas ni cambia la configuración de retención de la cuenta. Coordina cualquier limpieza con el propietario del recurso usando los nombres exactos de los blobs de esa ejecución.
Próximos pasos
Para hacer solicitudes HTTP directas en lugar de automatizarlas con la CLI, consulta la guía Exporta archivos a Azure con cURL. Ese es un flujo de trabajo REST independiente; su autenticación y su programa de subida no son necesarios aquí.
