Создание облачных ресурсов через CLI и jq
Идемпотентно управляйте CLI облачного провайдера и разбирайте ответы JSON для создания и удаления ресурсов
«Создание облачных ресурсов через CLI и jq» — бесплатный урок DevOps Bootcamp на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения DevOps Bootcamp, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс DevOps Bootcamp содержит 4 уроков всего.
Почему важны идемпотентные облачные скрипты
На уровне C2 написание облачных скриптов — это не нажатие кнопок, а создание кода, который можно безопасно запускать многократно, не создавая дубликаты ресурсов и не завершаясь с ошибкой при втором запуске.
Идемпотентный скрипт проверяет, существует ли ресурс, прежде чем создавать его. Это краеугольный камень надёжной автоматизации инфраструктуры.
- Облачные CLI (AWS, GCP, Azure) возвращают JSON — разбор этого вывода необходим.
jq— стандартный инструмент Unix для извлечения, фильтрации и преобразования JSON из скриптов оболочки.- Сочетание CLI + jq + условной логики позволяет писать надёжные и повторяемые скрипты подготовки ресурсов.
В этом уроке вы будете подготавливать корзины S3, экземпляры EC2 и роли IAM с помощью AWS CLI в качестве примера; описанные шаблоны напрямую применимы к gcloud и az.
Установка и проверка облачных CLI
Перед написанием скриптов убедитесь, что нужные инструменты установлены. В CI всегда фиксируйте версии, чтобы избежать расхождений между средами.
Приведённый ниже фрагмент проверяет наличие AWS CLI версии 2, jq и SDK GCP, устанавливая только отсутствующие компоненты. Этот шаблон полезен в скриптах начальной настройки новых виртуальных машин или контейнеров.
#!/usr/bin/env bash
set -euo pipefail
check_or_install() {
local cmd="$1"
local install_cmd="$2"
if ! command -v "$cmd" &>/dev/null; then
echo "[INFO] $cmd not found — installing..."
eval "$install_cmd"
else
echo "[OK] $cmd $("$cmd" --version 2>&1 | head -1)"
fi
}
# AWS CLI v2
check_or_install aws \
'curl -fsSL https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip -o /tmp/awscliv2.zip && unzip -q /tmp/awscliv2.zip -d /tmp && sudo /tmp/aws/install'
# jq
check_or_install jq \
'sudo apt-get install -y jq 2>/dev/null || sudo yum install -y jq'
# gcloud (optional)
check_or_install gcloud \
'echo "Install gcloud SDK manually from https://cloud.google.com/sdk"'
echo "All prerequisites satisfied."Запрос существующих ресурсов с помощью jq
Первый шаг любого идемпотентного скрипта — это чтение: запросите API, существует ли уже ресурс, а затем выберите дальнейшее действие.
AWS CLI всегда возвращает JSON. С помощью jq можно извлечь именно нужное поле:
jq -r '.Buckets[].Name'— необработанный строковый вывод, по одному имени корзины в строке.jq -e— завершается с кодом 1, если выражение возвращаетnullилиfalse, что делает его идеальным для защитных условийif.jq '.[] | select(.Name == env.BUCKET)'— фильтрация с помощью переменной оболочки черезenv.
Приведённый ниже фрагмент перечисляет все корзины S3 и проверяет, существует ли уже целевая корзина.
#!/usr/bin/env bash
set -euo pipefail
BUCKET="my-devops-artifacts-$(date +%Y%m)"
echo "Fetching existing S3 buckets..."
EXISTING=$(aws s3api list-buckets --output json)
# Extract names as newline-separated list
echo "$EXISTING" | jq -r '.Buckets[].Name'
# Check if target bucket exists
if echo "$EXISTING" | jq -e --arg b "$BUCKET" '.Buckets[] | select(.Name == $b)' > /dev/null 2>&1; then
echo "[EXISTS] Bucket $BUCKET already present — skipping creation."
else
echo "[MISSING] Bucket $BUCKET not found — will create."
fiИдемпотентное создание корзины S3
После добавления проверки существования оберните создание защитным условием. Хорошо структурированная облачная функция следует такому шаблону:
- Прочитайте текущее состояние через API.
- Сравните желаемое состояние с фактическим.
- Выполняйте действие только при наличии различий.
Обратите внимание на флаг --create-bucket-configuration — он обязателен для всех регионов, кроме us-east-1. Жёстко заданный регион внутри скрипта предотвращает незаметные сбои, если AWS_DEFAULT_REGION не установлена.
#!/usr/bin/env bash
set -euo pipefail
REGION="eu-west-1"
BUCKET="my-devops-artifacts-$(date +%Y%m)"
ensure_bucket() {
local bucket="$1"
local region="$2"
local existing
existing=$(aws s3api list-buckets --query 'Buckets[].Name' --output json)
if echo "$existing" | jq -e --arg b "$bucket" 'index($b) != null' > /dev/null 2>&1; then
echo "[SKIP] Bucket $bucket already exists."
return 0
fi
echo "[CREATE] Creating bucket $bucket in $region..."
aws s3api create-bucket \
--bucket "$bucket" \
--region "$region" \
--create-bucket-configuration LocationConstraint="$region"
# Enable versioning immediately after creation
aws s3api put-bucket-versioning \
--bucket "$bucket" \
--versioning-configuration Status=Enabled
echo "[DONE] Bucket $bucket created with versioning enabled."
}
ensure_bucket "$BUCKET" "$REGION"Разбор вложенного JSON: состояние экземпляра EC2
Ответы EC2 имеют глубокую вложенность. Обход путей с помощью jq и --query (JMESPath, встроенный в AWS CLI) работают одинаково, но jq мощнее при реализации сложной логики.
Основные шаблоны jq для EC2:
.Reservations[].Instances[]— сворачивает структуру с двумя массивами.select(.State.Name == "running")— фильтрует по состоянию..Tags[] | select(.Key == "Name") | .Value— извлекает значение тега.
Фрагмент находит работающий экземпляр по тегу Name и возвращает его ID и частный IP-адрес.
#!/usr/bin/env bash
set -euo pipefail
INSTANCE_NAME="web-server-prod"
RESULT=$(aws ec2 describe-instances \
--filters \
"Name=tag:Name,Values=${INSTANCE_NAME}" \
"Name=instance-state-name,Values=running" \
--output json)
# Extract instance ID and private IP using jq
INSTANCE_ID=$(echo "$RESULT" | jq -r \
'.Reservations[].Instances[] | .InstanceId')
PRIVATE_IP=$(echo "$RESULT" | jq -r \
'.Reservations[].Instances[] | .PrivateIpAddress')
if [[ -z "$INSTANCE_ID" ]]; then
echo "[WARN] No running instance named '$INSTANCE_NAME' found."
exit 1
fi
echo "Instance ID : $INSTANCE_ID"
echo "Private IP : $PRIVATE_IP"Идемпотентное создание роли IAM
Ресурсы IAM являются глобальными, поэтому их нельзя дублировать. AWS возвращает специальный код ошибки — EntityAlreadyExists — при попытке создать уже существующую роль. Перехват этого кода при работе с IAM в больших масштабах — более чистый шаблон идемпотентности, чем предварительный вызов для получения списка.
Приведённый ниже скрипт демонстрирует:
- Сохранение кода завершения CLI с помощью
|| true, чтобыset -eне прерывал выполнение. - Разбор JSON-сообщения об ошибке, которое AWS записывает в поток стандартной ошибки, с помощью подстановки процесса.
- Присоединение политики только в том случае, если она ещё не присоединена.
#!/usr/bin/env bash
set -euo pipefail
ROLE_NAME="DevOpsDeployRole"
POLICY_ARN="arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
TRUST_POLICY='{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "ec2.amazonaws.com" },
"Action": "sts:AssumeRole"
}]
}'
# Attempt creation; ignore EntityAlreadyExists
CREATE_OUTPUT=$(aws iam create-role \
--role-name "$ROLE_NAME" \
--assume-role-policy-document "$TRUST_POLICY" \
--output json 2>&1) || {
if echo "$CREATE_OUTPUT" | grep -q 'EntityAlreadyExists'; then
echo "[SKIP] Role $ROLE_NAME already exists."
else
echo "[ERROR] Unexpected error: $CREATE_OUTPUT" >&2
exit 1
fi
}
# Attach policy (attach-role-policy is idempotent by default)
aws iam attach-role-policy \
--role-name "$ROLE_NAME" \
--policy-arn "$POLICY_ARN"
echo "[OK] Role $ROLE_NAME ready with policy $POLICY_ARN."jq на продвинутом уровне: преобразования, отображение и toentries
Реальные ответы инфраструктуры содержат десятки полей. Преобразования jq позволяют изменить форму вывода для последующих инструментов, журналов или конфигурационных файлов.
Основные продвинутые шаблоны:
map(select(...))— отфильтровать массив, не потеряв оболочку массива.to_entries | map(select(.value != null))— удалить поля со значением null перед записью в конфигурацию.[.[] | {id: .InstanceId, ip: .PrivateIpAddress}]— спроецировать данные в новую форму.@csv,@tsv,@base64— встроенные преобразователи форматов.
Приведённый ниже фрагмент извлекает все работающие экземпляры и записывает файл инвентаризации в формате TSV.
#!/usr/bin/env bash
set -euo pipefail
OUTPUT_FILE="/tmp/ec2_inventory.tsv"
aws ec2 describe-instances \
--filters "Name=instance-state-name,Values=running" \
--output json \
| jq -r '
["InstanceId", "Name", "PrivateIp", "Type", "AZ"],
[
.Reservations[].Instances[] | [
.InstanceId,
(.Tags // [] | map(select(.Key == "Name")) | .[0].Value // "(none)"),
(.PrivateIpAddress // "N/A"),
.InstanceType,
.Placement.AvailabilityZone
]
][]
| @tsv' > "$OUTPUT_FILE"
echo "Inventory written to $OUTPUT_FILE:"
column -t "$OUTPUT_FILE"Ожидание асинхронных операций: опрос с помощью jq
Облачные операции выполняются асинхронно. Создание экземпляра EC2 немедленно возвращает состояние pending. Надёжные скрипты должны опрашивать состояние до достижения требуемого состояния, прежде чем продолжать работу.
Приведённый ниже шаблон использует цикл until с экспоненциальным увеличением интервала ожидания. AWS CLI также предоставляет подкоманды wait (например, aws ec2 wait instance-running), но самостоятельная реализация опроса даёт возможность настраивать время ожидания и вести более подробное журналирование.
#!/usr/bin/env bash
set -euo pipefail
INSTANCE_ID="i-0abcdef1234567890"
MAX_WAIT=300 # seconds
INTERVAL=10
ELAPSED=0
echo "Waiting for instance $INSTANCE_ID to reach 'running' state..."
while true; do
STATE=$(aws ec2 describe-instances \
--instance-ids "$INSTANCE_ID" \
--output json \
| jq -r '.Reservations[0].Instances[0].State.Name')
echo " [$(date +%T)] state = $STATE"
[[ "$STATE" == "running" ]] && break
if [[ "$STATE" == "terminated" || "$STATE" == "shutting-down" ]]; then
echo "[FATAL] Instance entered terminal state: $STATE" >&2
exit 1
fi
if (( ELAPSED >= MAX_WAIT )); then
echo "[TIMEOUT] Instance did not reach 'running' after ${MAX_WAIT}s." >&2
exit 1
fi
sleep "$INTERVAL"
(( ELAPSED += INTERVAL ))
done
echo "[OK] Instance $INSTANCE_ID is running."Мультиоблачный шаблон: аналоги GCP и Azure
Шаблон «прочитать, затем выполнить» напрямую переносится на другие облачные CLI. И gcloud, и az возвращают JSON и поддерживают фильтрацию:
- GCP:
gcloud ... --format='json'— передавайте данные вjqточно так же, как при работе с AWS. Используйтеgcloud ... --quiet, чтобы подавить запросы подтверждения в скриптах. - Azure:
az ... --output json— идентичный шаблон.az group existsвозвращает простую логическую строку (true/false), поэтому в простых случаяхjqне нужен.
Фрагмент показывает идемпотентное создание группы ресурсов в Azure и корзины GCS в GCP рядом, используя один и тот же шаблон защитного условия.
#!/usr/bin/env bash
set -euo pipefail
# --- Azure: idempotent resource group ---
RG="devops-rg"
LOCATION="westeurope"
if [[ $(az group exists --name "$RG") == "true" ]]; then
echo "[SKIP] Azure resource group $RG already exists."
else
echo "[CREATE] Creating Azure resource group $RG..."
az group create --name "$RG" --location "$LOCATION" --output json \
| jq '{name: .name, location: .location, provisioningState: .properties.provisioningState}'
fi
# --- GCP: idempotent GCS bucket ---
GCS_BUCKET="gs://devops-artifacts-prod"
PROJECT="my-gcp-project"
if gcloud storage buckets describe "$GCS_BUCKET" \
--project="$PROJECT" --format='value(name)' &>/dev/null; then
echo "[SKIP] GCS bucket $GCS_BUCKET already exists."
else
echo "[CREATE] Creating GCS bucket $GCS_BUCKET..."
gcloud storage buckets create "$GCS_BUCKET" \
--project="$PROJECT" \
--location=EU \
--uniform-bucket-level-access
fiУдаление: безопасное уничтожение ресурсов
Скрипты уничтожения не менее важны, чем скрипты создания. Безопасное удаление:
- Перечисляет ресурсы до удаления чего-либо и выводит сводку для проверки человеком.
- Принимает флаг
--dry-run, чтобы операторы могли подтвердить план без его выполнения. - Удаляет ресурсы в правильном порядке зависимостей (например, завершает экземпляры до удаления групп безопасности).
Приведённый ниже фрагмент завершает все экземпляры EC2 с тегом Env=staging с использованием защитного режима --dry-run.
#!/usr/bin/env bash
set -euo pipefail
DRY_RUN="${1:-}"
echo "Finding staging EC2 instances..."
INSTANCE_IDS=$(aws ec2 describe-instances \
--filters \
"Name=tag:Env,Values=staging" \
"Name=instance-state-name,Values=running,stopped" \
--output json \
| jq -r '[.Reservations[].Instances[].InstanceId] | @sh')
if [[ -z "$INSTANCE_IDS" ]]; then
echo "[INFO] No staging instances found. Nothing to do."
exit 0
fi
echo "Instances to terminate: $INSTANCE_IDS"
if [[ "$DRY_RUN" == "--dry-run" ]]; then
echo "[DRY-RUN] No changes made."
exit 0
fi
read -rp "Terminate these instances? [yes/N]: " CONFIRM
[[ "$CONFIRM" != "yes" ]] && { echo "Aborted."; exit 0; }
# shellcheck disable=SC2086
aws ec2 terminate-instances --instance-ids $INSTANCE_IDS --output json \
| jq '.TerminatingInstances[] | {id: .InstanceId, state: .CurrentState.Name}'
echo "[DONE] Termination initiated."От начала до конца: идемпотентный сценарий начальной настройки инфраструктуры
Объединяя всё воедино, сценарий начальной настройки, готовый для промышленной эксплуатации, координирует создание нескольких ресурсов в правильном порядке, полностью идемпотентен и выдаёт структурированные журналы, которые может обработать система непрерывной интеграции.
Продемонстрированы следующие основные практики:
- Структурированное журналирование с помощью вспомогательной функции
log(), добавляющей префиксы[INFO],[WARN],[ERROR]. - Файл состояния — записывайте идентификаторы созданных ресурсов в JSON-файл состояния, чтобы последующие запуски и сценарии удаления использовали одни и те же ссылки.
- Перехват ошибок —
trapперехватывает неожиданные завершения и сообщает номер строки, в которой произошла ошибка.
#!/usr/bin/env bash
set -euo pipefail
STATE_FILE="/tmp/infra_state.json"
REGION="eu-west-1"
BUCKET="devops-bootstrap-$(date +%Y%m)"
ROLE="BootstrapRole"
log() { echo "[$(date -u +%T)] [$1] ${*:2}"; }
trap 'log ERROR "Script failed at line $LINENO"' ERR
# Initialize state
[[ -f "$STATE_FILE" ]] || echo '{}' > "$STATE_FILE"
# --- Step 1: S3 bucket ---
EXISTING_BUCKETS=$(aws s3api list-buckets --query 'Buckets[].Name' --output json)
if echo "$EXISTING_BUCKETS" | jq -e --arg b "$BUCKET" 'index($b) != null' > /dev/null; then
log INFO "Bucket $BUCKET exists — skipping."
else
aws s3api create-bucket --bucket "$BUCKET" --region "$REGION" \
--create-bucket-configuration LocationConstraint="$REGION" > /dev/null
log INFO "Bucket $BUCKET created."
fi
# Update state file
jq --arg b "$BUCKET" '.bucket = $b' "$STATE_FILE" > /tmp/_state_tmp && mv /tmp/_state_tmp "$STATE_FILE"
# --- Step 2: IAM role ---
if aws iam get-role --role-name "$ROLE" &>/dev/null; then
log INFO "Role $ROLE exists — skipping."
else
aws iam create-role --role-name "$ROLE" \
--assume-role-policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"ec2.amazonaws.com"},"Action":"sts:AssumeRole"}]}' \
--output json | jq '{RoleName: .Role.RoleName, Arn: .Role.Arn}'
log INFO "Role $ROLE created."
fi
jq --arg r "$ROLE" '.role = $r' "$STATE_FILE" > /tmp/_state_tmp && mv /tmp/_state_tmp "$STATE_FILE"
log INFO "Bootstrap complete. State: $(cat "$STATE_FILE" | jq -c .)"Проверка знаний: защита от повторного выполнения с помощью jq
Проверьте, насколько Вы поняли принципы идемпотентного создания облачных ресурсов с помощью jq.
Повторение: создание сценариев облачных ресурсов с помощью CLI и jq
В этом уроке был рассмотрен полный жизненный цикл идемпотентной автоматизации облака с использованием сценариев оболочки, облачных интерфейсов командной строки и jq.
Основные принципы:
- Сначала чтение, затем запись — всегда сначала запрашивайте существующее состояние и действуйте только на основании различий.
- jq -e для проверок — используйте режим с кодом завершения, чтобы управлять ветвлениями
ifна основе ответов JSON. - Смысл кодов ошибок — перехватывайте специфичные для провайдера коды ошибок, например
EntityAlreadyExists, вместо предварительного получения списка, если это эффективнее. - Опрос асинхронного состояния — используйте циклы
untilс ограничением времени; никогда не предполагайте, что ресурс готов сразу после создания. - Файлы состояния — записывайте идентификаторы ресурсов в общий JSON-файл, чтобы каждый этап сценария и сценарий удаления использовали одни и те же ссылки.
- Флаги пробного запуска — всегда поддерживайте
--dry-run, чтобы оператор мог безопасно проверить действия перед удалением ресурсов.
Эти шаблоны, объединённые со строгим заголовком set -euo pipefail и перехватом ERR, формируют основу сценариев управления инфраструктурой промышленного уровня на уровне C2.
Часто задаваемые вопросы
Урок «Создание облачных ресурсов через CLI и jq» бесплатный?
Да — полный текст урока «Создание облачных ресурсов через CLI и jq» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс DevOps Bootcamp, подпишись на CoddyKit PRO. Курс DevOps Bootcamp содержит 4 уроков всего.
Чему я научусь в уроке «Создание облачных ресурсов через CLI и jq»?
Идемпотентно управляйте CLI облачного провайдера и разбирайте ответы JSON для создания и удаления ресурсов Ты практикуешь DevOps Bootcamp с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать DevOps Bootcamp?
Предыдущий опыт не требуется. DevOps Bootcamp на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Создание облачных ресурсов через CLI и jq»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке DevOps Bootcamp?
Да. Каждый урок DevOps Bootcamp включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Компактные Dockerfile и точки входа Shell
- Шаблонизация конфигураций с envsubst и heredoc
- Создание облачных ресурсов через CLI и jq
- Проверки состояния, барьеры готовности и циклы ожидания