# AMI com contrato: anatomia do novo bloqueio de tipos no EC2

Em 4 de setembro de 2026 o EC2 passou a aceitar `InstanceTypeSpecification` no AMI: duas listas, avaliadas no `RunInstances`, que bloqueiam o lançamento em tipo incompatível. O padrão move a compatibilidade do chamador para o artefato — e traz junto uma armadilha que quase ninguém vai ver antes de sangrar: a especificação casa strings, não capacidades de hardware.

- URL: https://fernando.moretes.com/blog/ami-com-contrato-anatomia-do-novo-bloqueio-de-tipos-no-ec2-amazon-ec2-n

- Markdown: https://fernando.moretes.com/blog/ami-com-contrato-anatomia-do-novo-bloqueio-de-tipos-no-ec2-amazon-ec2-n/article.md?lang=pt

- Published: 2026-09-04T23:04:36.982Z

- Category: IA & Agentes

- Tags: EC2, AMI, golden image, Auto Scaling, Graviton, Spot, governance, FinOps

- Reading time: 8 min

- Source: [Amazon EC2 now supports specifying compatible instance types on AMIs](https://aws.amazon.com/about-aws/whats-new/2026/09/ec2-images-supported-instances)

---

Um AMI sempre foi um contrato verbal. O artefato sabe exatamente de que hardware ele precisa — driver ENA, NVMe, arquitetura do binário, extensão de CPU que a JVM compilou — e nada disso estava escrito em lugar nenhum que o `RunInstances` pudesse ler. A partir de 4 de setembro de 2026 dá para escrever: o dono do AMI declara `SupportedInstanceTypes`, `UnsupportedInstanceTypes` ou os dois, e o EC2 bloqueia o lançamento fora da lista. Sem custo adicional, em todas as regiões. A leitura fácil é "menos launch quebrado". A leitura que interessa é outra — o ponto de enforcement mudou de lugar, e com ele mudou quem paga a conta da manutenção.

## O problema: o contrato existia, mas não era executável

Todo time de plataforma que publica golden AMI já viveu esta cena. A imagem endurecida foi construída para `x86_64`, com agente de EDR compilado estaticamente e um kernel que só carrega driver de rede em host Nitro. Alguém abre um Auto Scaling group com attribute-based instance type selection pedindo 4 vCPU e 16 GiB, deixa a diversificação de Spot larga — que é a recomendação correta para preencher capacidade — e o EC2 escolhe uma família Graviton porque ela casa os atributos e é a mais barata do momento.

A instância **sobe**. É esse o detalhe que faz o modo de falha doer. Ela não morre no `RunInstances`; ela morre depois, sem console, sem rede, sem nada além de um health check que expira. O ASG espera o grace period padrão de 300 segundos, marca a instância como não saudável, termina e tenta de novo — e como a decisão de alocação não mudou, ele escolhe o mesmo pool. Você tem um loop de launch/terminate cobrado no mínimo de 60 segundos por instância criada, um alarme que dispara às duas da manhã e um gráfico que parece falta de capacidade.

As defesas que existiam eram todas do lado do chamador: condição `ec2:InstanceType` em política IAM, SCP na Organization, `ExcludedInstanceTypes` dentro do `InstanceRequirements` da frota. Todas corretas, todas com o mesmo defeito estrutural — elas descrevem o que **aquele principal** pode lançar, não o que **aquela imagem** aguenta. Compartilhe o AMI com 40 contas e você precisa replicar a regra 40 vezes, em 40 lugares que divergem.

## O contrato viaja com o artefato — e falha no portão, não no health check

Desenho de referência: o pipeline gera a lista a partir de uma consulta de capacidade, grava a especificação no AMI, e o `RunInstances` vira o ponto único de avaliação para toda conta que recebeu a imagem.

### 🏗️ Pipeline de imagem — build time

- EC2 Image Builder pipeline do golden AMI (ci)
- DescribeInstanceTypes filtro por arquitetura + hypervisor (compute)
- ReplaceImageInstanceTypeSpecification lista explícita, gerada (ci)

### 🟧 AWS — plano de controle do EC2

- AMI InstanceTypeSpecification (storage)
- CopyImage / share especificação preservada (storage)
- RunInstances avalia tipo × contrato (security)

### 🚀 Consumidores — launch time

- Auto Scaling group ABIS + Spot diversificado (compute)
- Launch template versão do AMI fixada (compute)
- Instância lançada tipo dentro do contrato (compute)
- InvalidParameterCombination lançamento bloqueado (external)

### 🔍 Sinais — o que você tem que instrumentar

- CloudTrail errorCode em RunInstances (data)
- Scaling activity falha registrada no ASG (messaging)
- CloudWatch metric filter por imageId (data)

### Fluxos

- plat -> builder: publica o golden AMI
- builder -> capq: pergunta o que o hardware faz
- capq -> setspec: tipos compatíveis, enumerados
- setspec -> ami: grava as duas listas
- ami -> copy: cross-region / cross-account
- ami -> gate: contrato lido no lançamento
- asg -> lt: escala
- lt -> gate: RunInstances
- gate -> ok: tipo permitido
- gate -> blocked: tipo fora da lista
- blocked -> ct: erro auditável
- blocked -> sa: capacidade não entregue
- ct -> alarm: metric filter
- sa -> alarm

## Anatomia: duas listas, uma ordem de avaliação, zero estado

O atributo se chama `InstanceTypeSpecification` e carrega `SupportedInstanceTypes` e `UnsupportedInstanceTypes`. A lógica é curta o bastante para caber num ADR: sem especificação, tudo passa (é o default, e é por isso que nada quebrou no dia 4); só a lista de não suportados, passa tudo menos o que casar; com a lista de suportados, o tipo precisa casar a lista de suportados **e** não casar a de não suportados. A negação sempre vence.

Se escreve pela CLI com `aws ec2 replace-image-instance-type-specification --image-id ami-... --instance-type-specification '{"SupportedInstanceTypes": ["t3.*","a2.*"], "UnsupportedInstanceTypes": ["t3.micro"]}'`. Omitir o parâmetro remove a restrição. Se lê no `describe-images`, que devolve o campo `InstanceTypeSpecification` com os padrões como estão. Um lançamento barrado devolve `InvalidParameterCombination` no `RunInstances`, com a mensagem apontando o próprio `DescribeImages` como lugar de investigar.

Quatro propriedades mudam o desenho, e nenhuma delas está no anúncio:

**`Replace`, não `Modify`:** a operação troca a especificação inteira. Não existe adicionar nem remover item — é read-modify-write, sem versão condicional. Dois pipelines corrigindo a mesma imagem em paralelo produzem last-writer-wins **em silêncio**, e o perdedor só descobre quando um lançamento legítimo é bloqueado.

**Só o dono escreve.** Conta que recebeu o AMI compartilhado consome o contrato e não negocia.

**`CopyImage` preserva a especificação.** É o que faz o padrão valer a pena em topologia multi-região: você declara uma vez e a réplica em `sa-east-1` já nasce com o contrato.

**Marketplace é mutuamente exclusivo.** Não dá para pôr especificação em AMI listado no Marketplace, nem listar no Marketplace um AMI que já tem uma. Se você é ISV distribuindo appliance, este recurso não existe para você.

## Três formas de restringir tipo de instância — e o que cada uma cobre de verdade
| Critério | Condição IAM / SCP `ec2:InstanceType` | `ExcludedInstanceTypes` no ABIS | `InstanceTypeSpecification` no AMI |
| --- | --- | --- | --- |
| Onde a regra vive | Na política do principal, por conta ou OU | Na configuração da frota ou do ASG | No artefato, uma vez, na conta dona |
| Vale para quem lança de outra conta | Não — a política é de lá, não sua | Não — quem configura a frota é o consumidor | Sim — o EC2 avalia independente do chamador |
| Sobrevive a CopyImage e compartilhamento | Irrelevante — não acompanha a imagem | Irrelevante — não acompanha a imagem | Sim, preservada na cópia |
| Quando a falha aparece | `UnauthorizedOperation` no lançamento | Nunca — o tipo só não é escolhido | `InvalidParameterCombination` no lançamento |
| Custo de manutenção | Alto: replicar e auditar N contas | Médio: uma edição por frota que existir | Baixo por lugar, alto por geração de hardware |

## Onde o glob te trai: string não é predicado de arquitetura

As duas listas aceitam `*`. A documentação dá os exemplos: `t3.*` casa toda a família t3, `g5.*` casa a g5, `*.12xlarge` casa qualquer 12xlarge de qualquer família, `*xlarge` casa qualquer tipo xlarge ou maior. Parece expressivo. É onde eu pararia e faria a pergunta que importa: **você está descrevendo hardware ou está descrevendo nomes?**

O que o AMI realmente exige é uma capacidade — `arm64` contra `x86_64`, hypervisor Nitro contra Xen, presença de GPU, presença de disco local NVMe. O que a especificação avalia é um padrão sobre a string do tipo. As duas coisas coincidem *hoje* porque a AWS batiza famílias Graviton com `g` e famílias com disco local com `d`. Elas coincidem por convenção, e convenção de nomenclatura não é contrato: `c6gn`, `c7gd`, `m7i-flex`, `u7in-32tb` e `mac2-m2pro` já mostram que o esquema tem exceções, sufixos e infixos que nenhum glob simples cobre. Um `*g.*` escrito para excluir Graviton é uma heurística — e heurística escrita à mão, num arquivo que ninguém revisita, é dívida com juros.

A segunda armadilha é temporal, e é a mais cara. Uma **allow-list explícita bloqueia toda geração futura por construção**. Famílias novas chegam a novas regiões o tempo todo; no dia em que a próxima geração aparecer no seu pool de Spot, a frota que você deixou larga de propósito — porque diversificação larga é a prática correta para capacidade Spot — vai ignorar exatamente a família mais barata e mais disponível. Você não vai receber erro nenhum: o ASG simplesmente não escolhe, o preço sobe um pouco, a taxa de interrupção sobe um pouco, e ninguém liga isso a um JSON escrito oito meses atrás. É a mesma tensão do `OnDemandMaxPricePercentageOverLowestPrice`, que já vem com 20% de folga por padrão justamente para não estreitar o pool sem avisar.

> **Gere a lista, não escreva a lista:** O único jeito de manter o contrato honesto é derivá-lo de uma consulta de capacidade no mesmo build que produziu a imagem: `aws ec2 describe-instance-types --filters Name=processor-info.supported-architectures,Values=x86_64 Name=hypervisor,Values=nitro --query 'InstanceTypes[].InstanceType'` e alimentar o resultado direto no `replace-image-instance-type-specification`. Para frotas que já pensam em atributos, `GetInstanceTypesFromInstanceRequirements` faz o mesmo papel e te deixa pré-visualizar o conjunto antes de gravar. A lista fica explícita, verbosa e feia — e é exatamente isso que você quer, porque ela é **regenerada a cada build**, e o atraso em relação ao catálogo da AWS passa a ser a sua cadência de pipeline, não a memória de quem escreveu o glob.

## O hard block em dia de incidente — e o sinal que ninguém instrumenta

A documentação é explícita: o EC2 aplica a especificação como bloqueio duro, e launch templates e Auto Scaling groups que apontem para o AMI podem falhar se o tipo configurado não for permitido. Isso não é um detalhe de implementação — é a troca central do padrão. Você converteu uma falha lenta e cara (instância zumbi, health check, loop) numa falha rápida e barata (`InvalidParameterCombination` no `RunInstances`). Rápida e barata é melhor em 95% dos dias. Nos outros 5%, você está no meio de um evento de capacidade zonal, o `price-capacity-optimized` finalmente encontrou um pool com capacidade, e o seu contrato diz não.

Em sistema financeiro isso tem nome: você trocou risco de correção por risco de disponibilidade, e trocou de propósito. Quando o AMI de fato não boota naquele hardware, a troca é trivialmente boa — a instância não ia servir tráfego mesmo. Quando a lista só está desatualizada, você inventou um modo de indisponibilidade que não existia antes, exatamente na janela em que a elasticidade importa. É por isso que **allow-list gerada** e **deny-list mínima** não são preferência estética: uma erra para o lado de deixar passar, a outra erra para o lado de barrar.

O buraco é observabilidade. A especificação não afeta instância que já está rodando, não emite métrica própria e não aparece em dashboard nenhum por default. O sinal auditável é o evento `RunInstances` no CloudTrail com `errorCode` — dá para fazer metric filter agrupando por `requestParameters.imageId` e alarmar em qualquer bloqueio, porque em regime normal a taxa esperada é **zero**. Bloqueio recorrente não é o controle funcionando; é um launch template apontando para um AMI que mudou de contrato debaixo dele. E adicione o par de leitura no runbook: `describe-images` com o campo `InstanceTypeSpecification` é a primeira coisa a olhar quando um lançamento falha por parâmetro — a própria mensagem de erro manda você para lá, e quem estiver de plantão às duas da manhã precisa saber disso antes de abrir um caso no suporte.

## Como estragar isso

- **Aplicar o contrato em AMI já compartilhado sem inventário de consumidores**: você não vê os launch templates das outras 40 contas, e o bloqueio só aparece no próximo scale-out delas — que provavelmente é um pico, não um deploy.
- **Escrever a lista à mão**: um glob curado por humano é uma foto do catálogo de instâncias no dia em que foi escrito, e o catálogo muda toda semana.
- **Dois pipelines escrevendo a especificação do mesmo AMI**: `ReplaceImageInstanceTypeSpecification` substitui tudo e não tem escrita condicional; o segundo apaga o primeiro sem erro.
- **Usar isso como controle de custo**: bloquear `*.24xlarge` para conter gasto é política de FinOps disfarçada de compatibilidade — isso pertence a SCP com `ec2:InstanceType` ou a `OnDemandMaxPricePercentageOverLowestPrice`, onde a exceção tem dono e processo.
- **Confundir isso com validação em pipeline**: o contrato falha no lançamento, não no build. Ele não substitui um teste que sobe a imagem em cada família permitida antes de promover o AMI a produção.

## Lendo o padrão pelos pilares

- **operational-excellence**: O contrato vira código gerado no mesmo pipeline do AMI, versionado com ele. Adicione `describe-images` ao runbook: a mensagem de erro aponta para lá, e é o primeiro passo do diagnóstico.
- **security**: Restringir a lista não é isolamento nem menor privilégio — só o dono escreve, e a regra segue a imagem, mas ela não substitui condição IAM nem SCP para governança de quem lança o quê.
- **reliability**: Ganho real ao eliminar o loop de launch/terminate por AMI incompatível; risco real de não escalar em evento de capacidade se a lista estiver desatualizada. Alarme com taxa esperada zero é o que separa os dois.
- **cost**: O recurso não custa nada e corta o desperdício das instâncias que subiam para morrer no health check — mas uma allow-list estreita empurra a frota para pools mais caros sem emitir sinal nenhum.

> **O que eu faria:** Eu ligaria isso primeiro nos AMIs que realmente têm um limite físico — imagem `arm64`, imagem com driver que só existe em Nitro, imagem de inferência que assume GPU — e deixaria em paz os AMIs de propósito geral, onde o default permissivo já está certo. Em toda imagem que restringir, eu geraria a lista por `describe-instance-types` dentro do build e falharia o pipeline se ela ficasse vazia, porque uma lista vazia por bug de query bloqueia tudo silenciosamente. E colocaria um alarme de taxa esperada zero em cima do `errorCode` do `RunInstances` no CloudTrail no mesmo dia — não na sprint seguinte. A lição que me custou caro, em plataforma financeira: todo controle que barra alguma coisa e não conta quantas vezes barrou vira, com o tempo, uma causa raiz de incidente que ninguém consegue nomear. O bloqueio é a parte fácil; o contador é o que te salva às duas da manhã.

## Referências

- [AWS What's New — Amazon EC2 now supports specifying compatible instance types on AMIs (Sep 4, 2026)](https://aws.amazon.com/about-aws/whats-new/2026/09/ec2-images-supported-instances/)
- [Amazon EC2 User Guide — AMI allowed instance types (evaluation logic, wildcards, considerations)](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ami-allowed-instance-types.html)
- [Amazon EC2 API Reference — ReplaceImageInstanceTypeSpecification](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_ReplaceImageInstanceTypeSpecification.html)
- [AWS CLI Reference — ec2 replace-image-instance-type-specification](https://docs.aws.amazon.com/cli/latest/reference/ec2/replace-image-instance-type-specification.html)
- [EC2 Auto Scaling — attribute-based instance type selection, price and performance protection](https://docs.aws.amazon.com/autoscaling/ec2/userguide/create-mixed-instances-group-attribute-based-instance-type-selection.html)
- [Amazon EC2 User Guide — attribute-based instance type selection for EC2 Fleet and Spot Fleet](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-fleet-attribute-based-instance-type-selection.html)
- [Amazon EC2 API Reference — GetInstanceTypesFromInstanceRequirements](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_GetInstanceTypesFromInstanceRequirements.html)

## Veredito

Adote — com escopo estreito e com a lista gerada. Use `InstanceTypeSpecification` quando três condições valerem juntas: o AMI tem uma incompatibilidade de hardware real e não uma preferência de custo, você não controla todas as contas que lançam a imagem, e o pipeline que constrói o AMI consegue derivar a lista de uma consulta de capacidade a cada build. Faltando qualquer uma delas, a condição `ec2:InstanceType` em SCP continua sendo a ferramenta certa — ela erra do lado de quem sabe pedir exceção. Não use isso como controle de FinOps, não escreva glob à mão e não ligue em AMI compartilhado sem antes rodar `describe-images` contra os launch templates que já apontam para ele. O recurso não custa nada e o default permissivo garante que nada quebre até você agir; o preço é integralmente de manutenção, e ele vence toda vez que a AWS lançar uma família nova. Se o seu time não tem onde pendurar essa manutenção, o padrão continua correto e a resposta ainda é não.

**Rating:** Adopt with scope
