0Pricing
Linux Command Line & Bash Scripting Mastery · Aula

Criação de scripts para recursos de nuvem com CLI e jq

Controle CLIs de provedores de nuvem de forma idempotente e analise respostas JSON para provisionar e desmontar recursos.

Criação de scripts para recursos de nuvem com CLI e jq é uma aula grátis de Linux Command Line & Bash Scripting Mastery no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Linux Command Line & Bash Scripting Mastery, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Linux Command Line & Bash Scripting Mastery inclui 4 aulas no total.

Por que a criação de scripts idempotentes para a nuvem importa

No nível C2, criar scripts para a nuvem não significa clicar em botões — significa escrever código que possa ser executado com segurança várias vezes sem criar recursos duplicados nem falhar na segunda execução.

Um script idempotente verifica se um recurso já existe antes de criá-lo. Esse é o fundamento da automação confiável da infraestrutura.

  • As CLIs de nuvem (AWS, GCP, Azure) retornam JSON — analisar essa saída é essencial.
  • jq é a ferramenta Unix padrão para recortar, filtrar e transformar JSON em scripts de shell.
  • Combinar CLI, jq e lógica condicional permite escrever scripts de provisionamento robustos e repetíveis.

Ao longo desta lição, você provisionará depósitos S3, instâncias EC2 e funções IAM usando a AWS CLI como referência, com padrões que podem ser transferidos diretamente para gcloud e az.

Instalando e verificando CLIs de nuvem

Antes de criar scripts, confirme se as ferramentas corretas estão disponíveis. No CI, fixe sempre as versões para evitar diferenças entre os ambientes.

O trecho abaixo verifica a AWS CLI v2, o jq e o SDK do GCP, instalando apenas o que estiver faltando — um padrão útil em scripts de inicialização para máquinas virtuais ou contêineres novos.

#!/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."

Consultando recursos existentes com jq

O primeiro passo de qualquer script idempotente é uma leitura — consulte a API para saber se o recurso já existe e, em seguida, escolha o caminho adequado.

A AWS CLI sempre retorna JSON. O jq permite extrair exatamente o campo de que você precisa:

  • jq -r '.Buckets[].Name' — saída como cadeia de caracteres bruta, com um nome de depósito por linha.
  • jq -e — termina com o código 1 se a expressão produzir null ou false, sendo ideal para proteções com if.
  • jq '.[] | select(.Name == env.BUCKET)' — filtre usando uma variável do shell por meio de env.

O trecho abaixo lista todos os depósitos do S3 e verifica se um depósito de destino já existe.

#!/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

Criação idempotente de depósitos S3

Com a verificação de existência implementada, envolva a criação em uma proteção. Uma função de nuvem bem estruturada segue este padrão:

  1. Leia o estado atual na API.
  2. Compare o estado desejado com o estado real.
  3. Aja somente sobre a diferença.

Observe a opção --create-bucket-configuration — ela é necessária em todas as regiões, exceto us-east-1. Fixar a região no script evita falhas silenciosas quando AWS_DEFAULT_REGION não está definida.

#!/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"

Analisando JSON aninhado: estado da instância EC2

As respostas do EC2 são profundamente aninhadas. A navegação por caminhos do jq e --query (JMESPath, nativo da AWS CLI) funcionam — mas o jq é mais poderoso para lógicas complexas.

Padrões importantes de jq para EC2:

  • .Reservations[].Instances[] — achate a estrutura de matriz dupla.
  • select(.State.Name == "running") — filtre pelo estado.
  • .Tags[] | select(.Key == "Name") | .Value — extraia o valor de uma etiqueta.

O trecho encontra uma instância em execução pela etiqueta Name e retorna seu ID e IP privado.

#!/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"

Provisionamento idempotente de funções IAM

Os recursos do IAM são globais e não devem ser duplicados. A AWS retorna um código de erro específico — EntityAlreadyExists — quando você tenta criar uma função que já existe. Capturar esse código é um padrão de idempotência mais limpo do que fazer uma chamada de pré-verificação para listar recursos ao trabalhar com IAM em grande escala.

O script abaixo demonstra:

  • Capturar o código de saída da CLI com || true para impedir que set -e interrompa a execução.
  • Analisar o JSON da mensagem de erro que a AWS grava na saída de erro padrão usando substituição de processo.
  • Anexar uma política somente se ela ainda não estiver anexada.
#!/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 avançado: transformações, mapeamentos e toentries

As respostas reais da infraestrutura contêm dezenas de campos. As transformações do jq permitem remodelar a saída para ferramentas posteriores, registros ou arquivos de configuração.

Padrões avançados essenciais:

  • map(select(...)) — filtre uma matriz sem perder o contêiner da matriz.
  • to_entries | map(select(.value != null)) — remova campos nulos antes de gravar em uma configuração.
  • [.[] | {id: .InstanceId, ip: .PrivateIpAddress}] — projete os dados em uma nova estrutura.
  • @csv, @tsv, @base64 — conversores de formato integrados.

O trecho abaixo extrai todas as instâncias em execução e grava um arquivo de inventário 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"

Aguardando operações assíncronas: consulta repetida com jq

As operações de nuvem são assíncronas. A criação de uma instância EC2 retorna imediatamente com o estado pending. Scripts confiáveis precisam consultar repetidamente até que o estado desejado seja alcançado antes de continuar.

O padrão abaixo usa um laço until com recuo exponencial. A AWS CLI também fornece subcomandos wait (por exemplo, aws ec2 wait instance-running), mas a consulta repetida implementada manualmente oferece controle personalizado do tempo limite e registros mais detalhados.

#!/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."

Padrão multinuvem: equivalentes de GCP e Azure

O padrão idempotente de ler e depois agir pode ser aplicado diretamente a outras CLIs de nuvem. Tanto gcloud quanto az retornam JSON e oferecem suporte a filtros:

  • GCP: gcloud ... --format='json' — canalize para jq exatamente como faria com a AWS. Use gcloud ... --quiet para suprimir perguntas de confirmação nos scripts.
  • Azure: az ... --output json — o padrão é idêntico. az group exists retorna uma cadeia booleana simples (true/false), dispensando o jq em casos simples.

O trecho mostra, lado a lado, a criação idempotente de um grupo de recursos no Azure e de um depósito GCS no GCP usando o mesmo padrão de proteção.

#!/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

Destruição: remoção segura de recursos

Os scripts de destruição são tão importantes quanto os de criação. Uma remoção segura:

  • Lista os recursos antes de excluir qualquer coisa e exibe um resumo para análise humana.
  • Aceita uma opção --dry-run para que os operadores confirmem o plano sem executá-lo.
  • Exclui os recursos na ordem correta de dependência (por exemplo, encerra as instâncias antes de excluir os grupos de segurança).

O trecho abaixo encerra todas as instâncias EC2 marcadas com Env=staging usando uma proteção de simulação.

#!/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."

De ponta a ponta: script de inicialização de infraestrutura idempotente

Reunindo tudo: um script de inicialização pronto para produção orquestra vários recursos na ordem correta, é totalmente idempotente e emite registros estruturados que um sistema de integração contínua consegue analisar.

Práticas principais demonstradas:

  • Registro estruturado por meio de um auxiliar log() que acrescenta os prefixos [INFO], [WARN], [ERROR].
  • Arquivo de estado — grave os IDs dos recursos criados em um arquivo JSON de estado para que as execuções subsequentes e os scripts de desmontagem compartilhem as mesmas referências.
  • Captura de erros — trap captura saídas inesperadas e informa o número da linha que falhou.
#!/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 .)"

Verificação de conhecimentos: proteção de idempotência com jq

Teste seus conhecimentos sobre a criação de scripts idempotentes para a nuvem com jq.

Recapitulação: criação de scripts para recursos de nuvem via CLI e jq

Esta lição abordou todo o ciclo de vida da automação idempotente de recursos de nuvem usando scripts de shell, interfaces de linha de comando da nuvem e jq.

Princípios fundamentais:

  • Leia antes de escrever — sempre consulte primeiro o estado existente; aja apenas sobre as diferenças.
  • jq -e para verificações — use o modo de status de saída para controlar ramificações de if a partir de respostas JSON.
  • Semântica dos códigos de erro — capture códigos de erro específicos do provedor, como EntityAlreadyExists, em vez de fazer uma listagem prévia quando isso for mais eficiente.
  • Consulte periodicamente o estado assíncrono — use laços until com limites de tempo; nunca presuma que um recurso está pronto imediatamente após a criação.
  • Arquivos de estado — grave os IDs dos recursos em um arquivo JSON compartilhado para que cada fase do script e a desmontagem compartilhem as mesmas referências.
  • Sinalizadores de simulação — sempre ofereça suporte a --dry-run para uma revisão segura pelo operador antes de ações destrutivas.

Esses padrões — combinados com um cabeçalho rigoroso set -euo pipefail e uma captura de ERR — formam a base da criação de scripts de infraestrutura prontos para produção em nível C2.

Perguntas Frequentes

A aula “Criação de scripts para recursos de nuvem com CLI e jq” é grátis?

Sim — o texto completo de “Criação de scripts para recursos de nuvem com CLI e jq” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Linux Command Line & Bash Scripting Mastery, atualize para CoddyKit PRO. O curso de Linux Command Line & Bash Scripting Mastery inclui 4 aulas no total.

O que vou aprender em “Criação de scripts para recursos de nuvem com CLI e jq”?

Controle CLIs de provedores de nuvem de forma idempotente e analise respostas JSON para provisionar e desmontar recursos. Você pratica Linux Command Line & Bash Scripting Mastery com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Linux Command Line & Bash Scripting Mastery?

Nenhuma experiência prévia é necessária. Linux Command Line & Bash Scripting Mastery no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.

Quanto tempo leva a aula “Criação de scripts para recursos de nuvem com CLI e jq”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Linux Command Line & Bash Scripting Mastery?

Sim. Cada aula de Linux Command Line & Bash Scripting Mastery inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Dockerfiles enxutos e pontos de entrada do Shell
  2. Criação de modelos de configurações com envsubst e heredocs
  3. Criação de scripts para recursos de nuvem com CLI e jq
  4. Sondagens de integridade, barreiras de prontidão e loops de espera
← Voltar para Linux Command Line & Bash Scripting Mastery