Skip to content

Instantly share code, notes, and snippets.

@denoww
Last active August 20, 2025 16:09
Show Gist options
  • Save denoww/4d75e0f73d877b932c9c32e3766bef7f to your computer and use it in GitHub Desktop.
Save denoww/4d75e0f73d877b932c9c32e3766bef7f to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
# criar_maquina_ec2.sh
######################################
# portaria + sc_linker + scsip
######################################
# curl -fsSL https://gist.githubusercontent.com/denoww/4d75e0f73d877b932c9c32e3766bef7f/raw | bash -s -- --instance_type t3.small --subnet-id subnet-04173f5d --get_ssh_key_par_name portaria_staging_ssh_pem_key --get_security_groups_by_tag_projetos "portaria" --save_tag_projetos_on_ec2 "portaria,sc_linker,scsip" --storage_gb 100 --memoria_swap_gb 20 --region us-east-1 --get_iam_role_by_name AdminAccess
######################################
######################################
# midia_indoor_player + socket-server-seucondominio
######################################
# curl -fsSL https://gist.githubusercontent.com/denoww/4d75e0f73d877b932c9c32e3766bef7f/raw | bash -s -- --instance_type t3.small --subnet-id subnet-04173f5d --get_ssh_key_par_name portaria_staging_ssh_pem_key --get_security_groups_by_tag_projetos "midia_indoor_player,socket-server-seucondominio" --save_tag_projetos_on_ec2 "midia_indoor_player,socket-server-seucondominio" --storage_gb 100 --memoria_swap_gb 20 --region us-east-1 --get_iam_role_by_name AdminAccess
######################################
######################################
# midia_indoor_player
######################################
# curl -fsSL https://gist.githubusercontent.com/denoww/4d75e0f73d877b932c9c32e3766bef7f/raw | bash -s -- --instance_type t3.small --subnet-id subnet-04173f5d --get_ssh_key_par_name portaria_staging_ssh_pem_key --get_security_groups_by_tag_projetos "midia_indoor_player" --save_tag_projetos_on_ec2 "midia_indoor_player" --storage_gb 250 --memoria_swap_gb 50 --region us-east-1 --get_iam_role_by_name AdminAccess
######################################
######################################
# sctunnel_server
######################################
# curl -fsSL https://gist.githubusercontent.com/denoww/4d75e0f73d877b932c9c32e3766bef7f/raw | bash -s -- --instance_type t3.medium --subnet-id subnet-04173f5d --get_ssh_key_par_name scTunnel --get_security_groups_by_tag_projetos "sctunnel_server" --save_tag_projetos_on_ec2 "sctunnel_server" --storage_gb 100 --memoria_swap_gb 20 --region us-east-1 --get_iam_role_by_name AdminAccess
######################################
######################################
# midia_indoor_player
######################################
# curl -fsSL https://gist.githubusercontent.com/denoww/4d75e0f73d877b932c9c32e3766bef7f/raw | bash -s -- --instance_type t3.small --subnet-id subnet-04173f5d --get_ssh_key_par_name portaria_staging_ssh_pem_key --get_security_groups_by_tag_projetos "midia_indoor_player" --save_tag_projetos_on_ec2 "midia_indoor_player" --storage_gb 250 --memoria_swap_gb 50 --region us-east-1 --get_iam_role_by_name AdminAccess
######################################
######################################
# scCameras
######################################
# curl -fsSL https://gist.githubusercontent.com/denoww/4d75e0f73d877b932c9c32e3766bef7f/raw | bash -s -- --instance_type t3.xlarge --subnet-id subnet-04173f5d --get_ssh_key_par_name portaria_staging_ssh_pem_key --get_security_groups_by_tag_projetos "scCameras,portaria" --save_tag_projetos_on_ec2 "scCameras" --storage_gb 60 --memoria_swap_gb 15 --region us-east-1 --get_iam_role_by_name AdminAccess
######################################
######################################
# socket-server-seucondominio
######################################
# curl -fsSL https://gist.githubusercontent.com/denoww/4d75e0f73d877b932c9c32e3766bef7f/raw | bash -s -- --instance_type t3.small --subnet-id subnet-04173f5d --get_ssh_key_par_name portaria_staging_ssh_pem_key --get_security_groups_by_tag_projetos "socket-server-seucondominio" --save_tag_projetos_on_ec2 "socket-server-seucondominio" --storage_gb 100 --memoria_swap_gb 20 --region us-east-1 --get_iam_role_by_name AdminAccess
######################################
set -euo pipefail
need() { command -v "$1" >/dev/null 2>&1 || { echo "Erro: comando '$1' não encontrado."; exit 1; }; }
need aws
need jq
need python3
REGION="${REGION:-}"
KEYPAIR_NAME="" # --get_ssh_key_par_name (resolve por nome exato)
SG_TOKEN="" # --get_security_groups_by_tag_projetos (procura na tag 'projetos')
PROJETOS_CSV="" # --projetos ou --save_tag_projetos_on_ec2
VOLUME_SIZE_GB=""
INSTANCE_TYPE=""
PROFILE_OPTS=()
IAM_INSTANCE_PROFILE=""
MEMORIA_SWAP_GB="" # --memoria_swap_gb <int>
USER_DATA_PARAM=() # será preenchido se swap solicitado
SUBNET_ID_ARG=""
AZ_ID_ARG=""
AZ_NAME_ARG=""
usage() {
cat <<EOF
Uso:
$0 --region <aws-region> --get_ssh_key_par_name <nome-exato-do-keypair> \\
--get_security_groups_by_tag_projetos <token-projetos> --projetos "csv" --storage_gb <inteiro> --memoria_swap_gb <int>
--subnet-id <subnet-xxxxxxxx> # (recomendado) lança na subnet exata
--az-id <use1-az6> # ou force pelo Availability Zone ID
--az <us-east-1d> # ou pelo nome da AZ (menos estável)
Exemplo:
$0 --region us-east-1 --get_ssh_key_par_name portaria_staging_ssh_pem_key \\
--get_security_groups_by_tag_projetos "portaria,scCameras" --projetos "portaria,sc_linker,scsip" --storage_gb 100
Opcionais:
--instance_type <tipo> # pula o menu (ex.: t3.medium)
--profile <aws-profile>
--subnet-id <subnet-xxxxxxxx> # (recomendado) lança na subnet exata
--az-id <use1-az6> # ou force pelo Availability Zone ID
--az <us-east-1d> # ou pelo nome da AZ (menos estável)
Compat:
--save_tag_projetos_on_ec2 "csv" # alias de --projetos
EOF
exit 1
}
while [[ $# -gt 0 ]]; do
case "$1" in
--subnet-id) SUBNET_ID_ARG="$2"; shift 2;;
--az-id) AZ_ID_ARG="$2"; shift 2;;
--az|--availability-zone) AZ_NAME_ARG="$2"; shift 2;;
--region) REGION="$2"; shift 2;;
--get_ssh_key_par_name) KEYPAIR_NAME="$2"; shift 2;;
--get_security_groups_by_tag_projetos) SG_TOKEN="$2"; shift 2;;
--projetos) PROJETOS_CSV="$2"; shift 2;;
--save_tag_projetos_on_ec2) PROJETOS_CSV="$2"; shift 2;; # alias
--storage_gb) VOLUME_SIZE_GB="$2"; shift 2;;
--instance_type) INSTANCE_TYPE="$2"; shift 2;;
--profile) PROFILE_OPTS+=(--profile "$2"); shift 2;;
--get_iam_role_by_name) IAM_INSTANCE_PROFILE="$2"; shift 2;;
--memoria_swap_gb) MEMORIA_SWAP_GB="$2"; shift 2;;
-h|--help) usage;;
*) echo "Arg desconhecido: $1"; usage;;
esac
done
[[ -z "${REGION}" ]] && { echo "Faltou --region"; usage; }
[[ -z "${KEYPAIR_NAME}" ]] && { echo "Faltou --get_ssh_key_par_name (nome exato do Key Pair)"; usage; }
[[ -z "${SG_TOKEN}" ]] && { echo "Faltou --get_security_groups_by_tag_projetos (token na tag 'projetos')"; usage; }
[[ -z "${PROJETOS_CSV}" ]] && { echo "Faltou --projetos (ex.: \"portaria,sc_linker,scsip\")"; usage; }
[[ -z "${VOLUME_SIZE_GB}" ]] && { echo "Faltou --storage_gb (ex.: 100)"; usage; }
[[ ! "${VOLUME_SIZE_GB}" =~ ^[0-9]+$ ]] && { echo "--storage_gb deve ser inteiro (GB)"; exit 1; }
if [[ -n "${MEMORIA_SWAP_GB}" ]]; then
if ! [[ "${MEMORIA_SWAP_GB}" =~ ^[0-9]+$ ]]; then
echo "--memoria_swap_gb deve ser inteiro (GB)"; exit 1
fi
fi
validate_subnet() {
local sid="$1"
local out
out="$(
aws ec2 describe-subnets --region "${REGION}" "${PROFILE_OPTS[@]}" \
--subnet-ids "$sid" \
--query 'Subnets[0].SubnetId' --output text 2>/dev/null \
|| true
)"
echo "$out"
return 0
}
# Retorna uma subnet da AZ (por ID ou por nome), preferindo a default da AZ
find_subnet_in_az() {
local az_filter_name="$1" # availability-zone-id | availability-zone
local az_value="$2"
# tenta a default-for-az=true nessa AZ
local sid
sid="$(aws ec2 describe-subnets --region "${REGION}" "${PROFILE_OPTS[@]}" \
--filters "Name=${az_filter_name},Values=${az_value}" "Name=default-for-az,Values=true" \
--query 'Subnets[0].SubnetId' --output text 2>/dev/null || true)"
if [[ -z "$sid" || "$sid" == "None" ]]; then
# pega a primeira subnet dessa AZ na VPC default
sid="$(aws ec2 describe-subnets --region "${REGION}" "${PROFILE_OPTS[@]}" \
--filters "Name=${az_filter_name},Values=${az_value}" \
--query 'Subnets[0].SubnetId' --output text 2>/dev/null || true)"
fi
echo "$sid"
}
# ---------- Pricing helpers ----------
region_to_location_name() {
case "$1" in
us-east-1) echo "US East (N. Virginia)";;
us-east-2) echo "US East (Ohio)";;
us-west-1) echo "US West (N. California)";;
us-west-2) echo "US West (Oregon)";;
sa-east-1) echo "South America (São Paulo)";;
eu-central-1) echo "EU (Frankfurt)";;
eu-west-1) echo "EU (Ireland)";;
eu-west-2) echo "EU (London)";;
ap-south-1) echo "Asia Pacific (Mumbai)";;
ap-southeast-1) echo "Asia Pacific (Singapore)";;
ap-southeast-2) echo "Asia Pacific (Sydney)";;
ap-northeast-1) echo "Asia Pacific (Tokyo)";;
*) echo ""; return 1;;
esac
}
PRICING_LOCATION="$(region_to_location_name "${REGION}" || true)"
# --- [PATCH] Pricing com timeout curto (2s) ---
get_price_per_hour() {
local instance_type="$1" location="$2"
[[ -z "${location}" ]] && { echo ""; return; }
# Se 'timeout' existir, limita a 2s; senão, tenta normal
local aws_cmd=(aws pricing get-products --region us-east-1 "${PROFILE_OPTS[@]}"
--service-code AmazonEC2
--filters \
Type=TERM_MATCH,Field=instanceType,Value="${instance_type}" \
Type=TERM_MATCH,Field=location,Value="${location}" \
Type=TERM_MATCH,Field=operatingSystem,Value="Linux" \
Type=TERM_MATCH,Field=tenancy,Value="Shared" \
Type=TERM_MATCH,Field=capacitystatus,Value="Used" \
Type=TERM_MATCH,Field=preInstalledSw,Value="NA"
--query 'PriceList[0]' --output text)
local products_json
if command -v timeout >/dev/null 2>&1; then
products_json="$(timeout 2s "${aws_cmd[@]}" 2>/dev/null || true)"
else
products_json="$("${aws_cmd[@]}" 2>/dev/null || true)"
fi
[[ -z "$products_json" || "$products_json" == "None" ]] && { echo ""; return; }
echo "$products_json" | jq -r \
'.terms.OnDemand | to_entries[0].value.priceDimensions | to_entries[0].value.pricePerUnit.USD' \
2>/dev/null || echo ""
}
fmt_price() {
local ph="$1"
[[ -z "${ph}" || "${ph}" == "null" ]] && { echo "N/D"; return; }
python3 - <<PY || echo "USD/h: ${ph} | USD/min: N/D"
ph = float("${ph}")
print(f"USD/h: {ph:.5f} | USD/min: {ph/60.0:.5f}")
PY
}
# --- [PATCH] Menu robusto: sempre imprime opções, preço é melhor-esforço ---
choose_instance_type_menu() {
local -a options=("t3.medium" "t3.large" "t3.micro" "t3.small" "t2.micro" "t3.xlarge")
echo "Escolha o tipo de instância:"
local i=1
for it in "${options[@]}"; do
# Melhor-esforço para preço; não bloqueia a linha
local ph=""
ph="$(get_price_per_hour "${it}" "${PRICING_LOCATION}")" || ph=""
local price_line
price_line="$(fmt_price "${ph}")"
printf " %d) %s %s\n" "$i" "${it}" "${price_line}"
((i++))
done
read -rp "Digite o número [1-${#options[@]}]: " idx
if ! [[ "${idx}" =~ ^[1-9][0-9]*$ ]] || (( idx < 1 || idx > ${#options[@]} )); then
echo "Opção inválida."; exit 1
fi
echo "${options[$((idx-1))]}"
}
# ---------- AMI Ubuntu LTS x86_64 ----------
get_latest_ubuntu_ami() {
local p2404="/aws/service/canonical/ubuntu/server/24.04/stable/current/amd64/hvm/ebs-gp3/ami-id"
local p2204="/aws/service/canonical/ubuntu/server/22.04/stable/current/amd64/hvm/ebs-gp3/ami-id"
set +e
local AMI_ID
AMI_ID="$(aws ssm get-parameter --name "${p2404}" --region "${REGION}" "${PROFILE_OPTS[@]}" \
--query 'Parameter.Value' --output text 2>/dev/null)"
local rc=$?
set -e
[[ $rc -ne 0 || -z "${AMI_ID}" || "${AMI_ID}" == "None" ]] && \
AMI_ID="$(aws ssm get-parameter --name "${p2204}" --region "${REGION}" "${PROFILE_OPTS[@]}" \
--query 'Parameter.Value' --output text)"
echo "${AMI_ID}"
}
# ---------- SG por tag 'projetos' contendo token ----------
csv_contains_jq='
.Tags as $t
| ($t[]? | select(.Key=="projetos") | .Value // "") as $val
| ($val | gsub("\\s";"")) as $v
| ($v | test("(^|,)" + $token + "(,|$)"))
'
find_sg_by_token() {
local token="$1"
aws ec2 describe-security-groups --region "${REGION}" "${PROFILE_OPTS[@]}" \
--filters "Name=tag-key,Values=projetos" \
--query 'SecurityGroups[].{GroupId:GroupId,GroupName:GroupName,Tags:Tags}' --output json \
| jq -r --arg token "$token" '
.[] | select('"${csv_contains_jq}"') | .GroupId
' | head -n1
}
# ---------- Subnet default ----------
find_default_subnet() {
aws ec2 describe-subnets --region "${REGION}" "${PROFILE_OPTS[@]}" \
--filters "Name=default-for-az,Values=true" \
--query 'Subnets[0].SubnetId' --output text
}
# ---------- Exec ----------
echo "[1/6] Validando Key Pair por NOME: '${KEYPAIR_NAME}'..."
KP_LOOKUP="$(aws ec2 describe-key-pairs --region "${REGION}" "${PROFILE_OPTS[@]}" \
--key-names "${KEYPAIR_NAME}" --query 'KeyPairs[0].KeyName' --output text 2>/dev/null || true)"
[[ -z "${KP_LOOKUP}" || "${KP_LOOKUP}" == "None" ]] && { echo "Key Pair '${KEYPAIR_NAME}' não encontrado por nome."; exit 1; }
echo " -> KeyName: ${KP_LOOKUP}"
# Descobre o VPC da subnet (para garantir SGs compatíveis)
get_vpc_id_from_subnet() {
aws ec2 describe-subnets --region "${REGION}" "${PROFILE_OPTS[@]}" \
--subnet-ids "$1" \
--query 'Subnets[0].VpcId' --output text
}
# Encontra TODOS os SGs cuja tag "projetos" contenha ao menos um dos tokens CSV,
# restringindo à VPC informada. Retorna IDs separados por espaço.
find_sgs_by_tokens_in_vpc() {
local tokens_csv="$1"
local vpc_id="$2"
local tokens_clean
tokens_clean="$(echo -n "${tokens_csv}" | tr -d '[:space:]')"
aws ec2 describe-security-groups --region "${REGION}" "${PROFILE_OPTS[@]}" \
--filters "Name=vpc-id,Values=${vpc_id}" "Name=tag-key,Values=projetos" \
--query 'SecurityGroups[].{GroupId:GroupId,Tags:Tags}' --output json \
| jq -r --arg tokens "${tokens_clean}" '
# lista de tokens (CSV -> array)
($tokens | split(",") | map(select(. != ""))) as $T
| .[]
| (.Tags // []) as $tags
| ($tags[]? | select(.Key=="projetos") | .Value // "" | gsub("\\s";"")) as $val
| if ($T | any(. as $tok | ($val | test("(^|,)" + $tok + "(,|$)")))) then .GroupId else empty end
' \
| sort -u | tr '\n' ' '
}
echo "[2/6] Buscando Ubuntu LTS x86_64 via SSM..."
AMI_ID="$(get_latest_ubuntu_ami)"
echo " -> AMI: ${AMI_ID}"
echo "[3/6] Resolvendo Subnet..."
if [[ -n "${SUBNET_ID_ARG}" ]]; then
SUBNET_ID="$(validate_subnet "${SUBNET_ID_ARG}")"
[[ -z "${SUBNET_ID}" || "${SUBNET_ID}" == "None" ]] && { echo "Subnet inválida: ${SUBNET_ID_ARG}"; exit 1; }
echo " -> SubnetId (forçada): ${SUBNET_ID}"
elif [[ -n "${AZ_ID_ARG}" ]]; then
SUBNET_ID="$(find_subnet_in_az 'availability-zone-id' "${AZ_ID_ARG}")"
[[ -z "${SUBNET_ID}" || "${SUBNET_ID}" == "None" ]] && { echo "Nenhuma subnet encontrada para AZ ID ${AZ_ID_ARG}"; exit 1; }
echo " -> SubnetId (por AZ ID ${AZ_ID_ARG}): ${SUBNET_ID}"
elif [[ -n "${AZ_NAME_ARG}" ]]; then
SUBNET_ID="$(find_subnet_in_az 'availability-zone' "${AZ_NAME_ARG}")"
[[ -z "${SUBNET_ID}" || "${SUBNET_ID}" == "None" ]] && { echo "Nenhuma subnet encontrada para AZ ${AZ_NAME_ARG}"; exit 1; }
echo " -> SubnetId (por AZ ${AZ_NAME_ARG}): ${SUBNET_ID}"
else
# Fallback antigo (default subnet da conta/região)
SUBNET_ID="$(aws ec2 describe-subnets --region "${REGION}" "${PROFILE_OPTS[@]}" \
--filters "Name=default-for-az,Values=true" \
--query 'Subnets[0].SubnetId' --output text)"
[[ -z "${SUBNET_ID}" || "${SUBNET_ID}" == "None" ]] && { echo "Sem subnet default. Use --subnet-id ou --az-id/--az."; exit 1; }
echo " -> SubnetId (default): ${SUBNET_ID}"
fi
echo "[4/6] Resolvendo Security Groups na VPC pela tag 'projetos' contendo [${SG_TOKEN}]..."
# NOVO: pegar a VPC da subnet e então listar TODOS os SGs por tokens (CSV)
VPC_ID="$(get_vpc_id_from_subnet "${SUBNET_ID}")"
echo " -> VpcId: ${VPC_ID}"
SG_IDS_STR="$(find_sgs_by_tokens_in_vpc "${SG_TOKEN}" "${VPC_ID}")"
[[ -z "${SG_IDS_STR// }" ]] && { echo "Não achei Security Groups em ${VPC_ID} com 'projetos' contendo algum de: ${SG_TOKEN}"; exit 1; }
echo " -> SecurityGroupIds: ${SG_IDS_STR}"
if [[ -z "${INSTANCE_TYPE}" ]]; then
echo "[5/6] Selecionando tipo de instância:"
INSTANCE_TYPE="$(choose_instance_type_menu)"
fi
echo " -> InstanceType: ${INSTANCE_TYPE}"
echo "[6/6] Criando instância..."
DATE_STR="$(date +%Y%m%d--%H%M)"
PROJETOS_SLUG="$(echo -n "${PROJETOS_CSV}" \
| tr -d '[:space:]' \
| sed -E 's/,/-/g; s/[^A-Za-z0-9._-]+/-/g; s/-+/-/g; s/^-//; s/-$//')"
NAME="NOVA-${PROJETOS_SLUG}-${DATE_STR}"
# normaliza: remove espaços do CSV e escapa \ , =
PROJETOS_CSV_CLEAN="$(echo -n "${PROJETOS_CSV}" | tr -d '[:space:]')"
PROJETOS_CSV_ESCAPED="$(printf '%s' "${PROJETOS_CSV_CLEAN}" \
| sed -e 's/\\/\\\\/g' -e 's/,/\\,/g' -e 's/=/\\=/g')"
INSTANCE_TAGS="[{Key=Name,Value=${NAME}},{Key=projetos,Value=${PROJETOS_CSV_ESCAPED}}]"
VOLUME_TAGS="${INSTANCE_TAGS}"
###################################################
# SWAP MEMÓRIA + ssm + cwagent
###################################################
# Se foi solicitado swap, cria user-data inline (CLI v2 faz base64 automático)
# Se foi solicitado swap, cria user-data inline (CLI v2 faz base64 automático)
# Se foi solicitado swap, cria user-data inline (CLI v2 faz base64 automático)
# --- USER DATA (sem expansão local) ---
# --- USER DATA (sem expansão local) ---
USER_DATA="$(cat <<'EOF'
#!/bin/bash
set -euxo pipefail
MEMORIA_SWAP_GB="__MEM_SWAP__"
# ---- helpers ----
apt_retry() {
export DEBIAN_FRONTEND=noninteractive
for i in {1..5}; do
if apt-get update -y && apt-get install -y "$@"; then
return 0
fi
echo "apt-get tentativa $i falhou; aguardando..."
sleep 5
done
apt-get update -y && apt-get install -y "$@"
}
# ---------- SWAP opcional ----------
if [[ -n "${MEMORIA_SWAP_GB}" && "${MEMORIA_SWAP_GB}" != "0" ]]; then
SWAPFILE="/swapfile"
SIZE_GB="${MEMORIA_SWAP_GB}"
if [ ! -f "$SWAPFILE" ]; then
if command -v fallocate >/dev/null 2>&1; then
fallocate -l "${SIZE_GB}G" "$SWAPFILE"
else
dd if=/dev/zero of="$SWAPFILE" bs=1G count="${SIZE_GB}" status=progress
fi
chmod 600 "$SWAPFILE"
mkswap "$SWAPFILE"
swapon "$SWAPFILE"
fi
grep -qE '^/swapfile\s' /etc/fstab || echo '/swapfile swap swap defaults 0 0' >> /etc/fstab
sysctl -w vm.swappiness=10
sed -i '/^vm\.swappiness=/d' /etc/sysctl.conf
echo 'vm.swappiness=10' >> /etc/sysctl.conf
fi
# ---------- SSM Agent ----------
apt_retry curl jq snapd
systemctl enable --now snapd || true
# aguarda o snapd "semear" para evitar race
snap wait system seed.loaded || true
snap install amazon-ssm-agent --classic || true
# habilita os dois nomes possíveis de serviço
systemctl enable --now snap.amazon-ssm-agent.amazon-ssm-agent || true
systemctl enable --now amazon-ssm-agent || true
# ---------- CloudWatch Agent (RAM) ----------
ARCH="$(dpkg --print-architecture)" # amd64/arm64
TMPD="$(mktemp -d)"
cd "$TMPD"
curl -fSLo amazon-cloudwatch-agent.deb "https://amazoncloudwatch-agent.s3.amazonaws.com/ubuntu/${ARCH}/latest/amazon-cloudwatch-agent.deb"
dpkg -i -E ./amazon-cloudwatch-agent.deb
install -d -m 0755 /opt/aws/amazon-cloudwatch-agent/etc
cat >/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json <<'JSON'
{
"agent": {
"metrics_collection_interval": 60,
"logfile": "/opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log"
},
"metrics": {
"namespace": "CWAgent",
"append_dimensions": {
"InstanceId": "${instance_id}",
"ImageId": "${image_id}",
"InstanceType": "${instance_type}"
},
"aggregation_dimensions": [["InstanceId"]],
"metrics_collected": {
"cpu": {
"measurement": ["cpu_usage_active"],
"metrics_collection_interval": 60,
"resources": ["*"],
"totalcpu": true
},
"mem": {
"measurement": ["mem_used_percent","mem_used","mem_total"],
"metrics_collection_interval": 60
},
"swap": {
"measurement": ["swap_used_percent"],
"metrics_collection_interval": 60
},
"disk": {
"resources": ["*"],
"measurement": ["used_percent"],
"metrics_collection_interval": 60,
"drop_device": true
}
}
}
}
JSON
/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \
-a fetch-config -m ec2 \
-c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json -s
systemctl enable --now amazon-cloudwatch-agent || true
echo "cwagent:ok $(date -Is)" >> /var/log/cloud-init-output.log
EOF
)"
# injeta o número vindo da CLI (ou vazio)
USER_DATA="${USER_DATA//__MEM_SWAP__/${MEMORIA_SWAP_GB:-}}"
USER_DATA_PARAM=(--user-data "$USER_DATA")
###################################################
###################################################
###################################################
RUN_JSON="$(aws ec2 run-instances --region "${REGION}" "${PROFILE_OPTS[@]}" \
--image-id "${AMI_ID}" \
--instance-type "${INSTANCE_TYPE}" \
--key-name "${KP_LOOKUP}" \
--security-group-ids ${SG_IDS_STR} \
--subnet-id "${SUBNET_ID}" \
${IAM_INSTANCE_PROFILE:+--iam-instance-profile Name=${IAM_INSTANCE_PROFILE}} \
"${USER_DATA_PARAM[@]}" \
--tag-specifications "ResourceType=instance,Tags=${INSTANCE_TAGS}" "ResourceType=volume,Tags=${VOLUME_TAGS}" \
--block-device-mappings "DeviceName=/dev/sda1,Ebs={VolumeSize=${VOLUME_SIZE_GB},VolumeType=gp3,DeleteOnTermination=true}" \
--metadata-options "HttpTokens=required,HttpEndpoint=enabled" \
--capacity-reservation-specification "CapacityReservationPreference=none" \
--count 1 \
--output json)" \
|| { rc=$?; echo "ERRO: aws ec2 run-instances falhou (rc=$rc)"; exit $rc; }
INSTANCE_ID="$(echo "${RUN_JSON}" | jq -r '.Instances[0].InstanceId')"
echo
echo "✅ Instância criada!"
echo " Name: ${NAME}"
echo " InstanceId: ${INSTANCE_ID}"
echo " Region: ${REGION}"
echo " Tipo: ${INSTANCE_TYPE}"
echo " AMI: ${AMI_ID}"
echo " Volume raiz: ${VOLUME_SIZE_GB} GB (gp3)"
echo
echo "Aguardando 'running'..."
aws ec2 wait instance-running --region "${REGION}" "${PROFILE_OPTS[@]}" --instance-ids "${INSTANCE_ID}"
aws ec2 describe-instances --region "${REGION}" "${PROFILE_OPTS[@]}" \
--instance-ids "${INSTANCE_ID}" \
--query 'Reservations[0].Instances[0].{State:State.Name,PublicIp:PublicIpAddress,PrivateIp:PrivateIpAddress,Tags:Tags}' \
--output table
echo
echo "DNS público:"
echo "aws ec2 describe-instances --region ${REGION} ${PROFILE_OPTS[*]} --instance-ids ${INSTANCE_ID} --query 'Reservations[0].Instances[0].PublicDnsName' --output text"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment