Por que apagar a VM não basta

Instâncias, discos, endereços IP e snapshots são recursos separados, com identificadores próprios e regras de ciclo de vida. Excluir uma VM não garante que os demais itens associados também sejam removidos. Por isso, um inventário de instâncias não substitui consultas aos recursos que podem continuar existindo e aparecendo na fatura.

Na AWS, o volume EBS raiz criado com a instância é apagado com ela. Para volumes de dados, o resultado depende de DeleteOnTermination e da forma de criação. O volume incluído no lançamento pelo console é preservado, enquanto o incluído pela CLI é apagado. Volumes anexados depois do lançamento também são preservados. Elastic IP é independente, e snapshots sobrevivem ao volume e à instância. Desde fevereiro de 2024, cada IPv4 público custa USD 0,005 por hora, em uso ou ocioso.

GCP: discos, endereços e snapshots

No GCP, cada disco anexado tem a opção autoDelete. O padrão é sim para o disco de boot criado com a instância. Para um disco existente anexado com --disk, o padrão é não. A referência do gcloud também alerta que, se o disco for desanexado depois, essa opção deixa de valer. Um endereço externo estático reservado aparece como RESERVED quando não está atribuído, e a tarifa sem uso é maior que a de um IP em uso. Snapshots guardam sourceDisk, mas não são apagados com o disco.

Azure: opções de exclusão e recursos soltos

No Azure, disco do sistema operacional, NIC e IP público são recursos separados, com deleteOption próprio. O padrão do disco do SO é Delete no portal e Detach na CLI ou no PowerShell; discos soltos aparecem como Unattached. Confira as opções de cada recurso, pois a remoção da VM não implica que todos os recursos associados serão excluídos.

OCI: volumes e IPs reservados

Na OCI, boot volumes são preservados por padrão ao terminar a instância. Block volumes ficam AVAILABLE, estado que também pode indicar um volume anexado. IPs reservados sem atribuição têm assignedEntityId nulo. Por isso, o estado do volume, isoladamente, não identifica se ele está solto; é necessário cruzar a lista de volumes com a de attachments ativos.

Inventário somente leitura: AWS e GCP

Os comandos a seguir consultam recursos sem apagá-los. Eles mostram identificadores, tamanho, criação, associação ou origem, dados úteis para uma investigação posterior. Para snapshots da AWS, compare o VolumeId com os volumes existentes.

AWS: volumes sem instância, Elastic IPs sem associação e snapshots próprios com o volume de origem

aws ec2 describe-volumes --filters Name=status,Values=available --query "Volumes[].{id:VolumeId,gb:Size,created:CreateTime,tags:Tags}"

aws ec2 describe-addresses --query "Addresses[?AssociationId==null].[PublicIp,AllocationId]"

aws ec2 describe-snapshots --owner-ids self --query "Snapshots[].[SnapshotId,VolumeId,VolumeSize,StartTime]"

cruzar o VolumeId de cada snapshot com describe-volumes: se não estiver lá, o disco já não existe

GCP: discos sem usuários, endereços estáticos reservados sem atribuição e snapshots com o disco de origem

gcloud compute disks list --filter="-users:*" --format="table(name,zone.basename(),sizeGb,creationTimestamp)"

gcloud compute addresses list --filter="status=RESERVED"

gcloud compute snapshots list --format="table(name,sourceDisk.basename(),diskSizeGb,creationTimestamp)"

Inventário somente leitura: Azure e OCI

Azure: discos Unattached, IPs públicos sem ipConfiguration e snapshots com o recurso de origem

az disk list --query "[?diskState=='Unattached'].{name:name,rg:resourceGroup,gb:diskSizeGb,created:timeCreated}" -o table

az network public-ip list --query "[?ipConfiguration==null].{name:name,rg:resourceGroup,ip:ipAddress}" -o table

az snapshot list --query "[].{name:name,source:creationData.sourceResourceId,created:timeCreated}" -o table

OCI: AVAILABLE não significa solto; o cruzamento é com os attachments

oci bv volume list --compartment-id "$C" --lifecycle-state AVAILABLE --all --query 'data[].{id:id,name:"display-name",gb:"size-in-gbs",created:"time-created"}'

oci compute volume-attachment list --compartment-id "$C" --all --query 'data[?"lifecycle-state"==ATTACHED]."volume-id"'

boot volumes que sobreviveram a um terminate: mesmo cruzamento, por availability domain

oci bv boot-volume list --compartment-id "$C" --availability-domain "$AD" --all

oci compute boot-volume-attachment list --compartment-id "$C" --availability-domain "$AD" --all

IPs públicos reservados sem atribuição

oci network public-ip list --compartment-id "$C" --scope REGION --lifetime RESERVED --all --query 'data[?"assigned-entity-id"==null].{ip:"ip-address",state:"lifecycle-state"}'

Na OCI, AVAILABLE não permite distinguir um volume solto de um anexado. É preciso cruzar a lista de volumes com os attachments. Nas quatro nuvens, faça uma consulta própria para IPs: eles têm ciclo de vida separado e podem custar mesmo sem uso.

Interpretação e revisão antes da limpeza

Uma listagem identifica recursos sem associação, mas não prova que sejam lixo. Ela não informa quando o disco foi desanexado nem quem o criou. CreateTime registra a criação, não a data do detach. Para uma carência de 14 ou 30 dias, registre quando o recurso foi visto pela primeira vez, por exemplo com uma tag ou uma tabela mantida entre execuções.

A autoria pode exigir consulta ao audit log, e recursos antigos sem tags precisam ser reconhecidos por alguém. Um PersistentVolume do Kubernetes com persistentVolumeReclaimPolicy: Retain sobrevive à exclusão do PVC; o disco pode ser um backup, não um órfão descartável. O texto também cita discos de banco mantidos de propósito. O script cloud-orphans lista discos e IPs nas quatro nuvens, mas não apaga nada: a saída contra OCI era real e as demais usavam dados de teste. A decisão de excluir exige revisão humana.

Confira mais em: https://guiadeti.com.br/noticias/o-que-sobrevive-quando-voce-apaga-uma-vm-discos-ips-reservados-snapshots/