Post

Azure Image Builder na prática: Windows Server 2022/2025 e Linux Ubuntu 24.04 e Debian 13

Azure Image Builder na prática: Windows Server 2022/2025 e Linux Ubuntu 24.04 e Debian 13

Fala pessoALL! Chegamos pra mais um super conteúdo pra toda comunidade!

Seguimos evoluindo e agora para o próximo nível o tema será Golden Images. Para a criação de uma imagem padronizada para Windows (Server ou Client) e Linux, é muito comum começarmos pelo caminho mais simples: Diretamente no portal do Azure escolhemos uma imagem do Marketplace, definimos o tamanho do disco, definimos o usuário padrão, senha ou chave SSH (caso escolha o Linux), executamos um snapshot, realizamos a captura, publicamos em uma galeria e pronto, temos uma Golden Image funcional e pronta para uso.

E isso funciona muito bem para laboratório, estudo, testes rápidos e até mesmo a padronização de grandes empresas, mas quando começamos a falar de ambiente corporativo de alto padrão, governança, segurança, atualização recorrente e escala, esse modelo começa a ficar um pouco limitado por ser muito manual.

Agora imagine o seguinte cenário:

  1. Toda VM Windows precisa nascer com alguns pacotes básicos;
  2. Toda VM Linux precisa nascer atualizada, com timezone correto e ferramentas mínimas;
  3. Algumas imagens precisam ter agentes corporativos;
  4. As imagens precisam ser versionadas;
  5. O time precisa conseguir criar novas VMs sempre a partir de uma base padronizada.

É um tema sensível e trabalhoso, mas é exatamente aqui que entra o Azure Image Builder.

Neste artigo construiremos juntos na prática uma estrutura de imagens customizadas no Azure usando o Azure Image Builder, definiremos onde ficarão todas as Golden Imagens dentro do Azure Compute Gallery. Focaremos principalmente nos S.Os mais utilizados em todo o mundo:

  • Windows Server 2022;
  • Windows Server 2025;
  • Ubuntu 24.04 LTS;
  • Debian 13.

Vou seguir um modelo um pouco mais completo usando VNet dedicada para o processo de build. Existe também o caminho mais simples, sem VNet customizada, mas para um conteúdo mais técnico e próximo de ambiente corporativo, vale a pena já entender o fluxo mais controlado.


Mas antes de começarmos, o que é e pra que serve o Azure VM Image Builder?

O Azure VM Image Builder é um serviço do Azure que permite automatizar a criação de imagens customizadas de máquinas virtuais. Na prática ele pega uma imagem base e executa as customizações de forma automatizada e em seguida publica o resultado em algum destino, armazenando em:

  1. Managed Image;
  2. VHD;
  3. Azure Compute Gallery.

Neste artigo decidi focar somente no Azure Compute Gallery, simplesmente porque a Azure Compute Gallery permite:

  • Criar definições de imagem;
  • Versionar imagens;
  • Replicar imagens entre regiões;
  • Reutilizar imagens em múltiplos deployments;
  • Padronizar Windows e Linux;
  • Ter um modelo mais profissional de Golden Image.

De forma bem resumida, o fluxo fica assim:

flowchart TB
    A["Matriz de Imagens<br/>Azure Image Builder"]

    A --> W["Windows Server"]
    A --> L["Linux"]

    W --> W22["Windows Server 2022"]
    W --> W25["Windows Server 2025"]

    L --> U24["Ubuntu 24.04"]
    L --> D13["Debian 13"]

    W22 --> C1["Customizações Windows"]
    W25 --> C1

    U24 --> C2["Customizações Linux"]
    D13 --> C2

    C1 --> G["Azure Compute Gallery"]
    C2 --> G

    G --> V["Versões das Imagens"]
    V --> VM["VMs Padronizadas"]

O que vamos construir neste laboratório?

Neste laboratório vamos criar quatro imagens customizadas:

Sistema OperacionalImagem baseDestino
Windows Server 2022Windows Server 2022 Datacenter: Azure EditionAzure Compute Gallery
Windows Server 2025Windows Server 2025 Datacenter: Azure EditionAzure Compute Gallery
Ubuntu 24.04 LTSUbuntu 24.04 LTS Server Gen2Azure Compute Gallery
Debian 13Debian 13 Gen2Azure Compute Gallery

Todas as definições serão criadas como:

1
2
3
Generalized
Generation 2
TrustedLaunchSupported

Uma imagem Generalized não mantém informações específicas da máquina utilizada durante a criação, como nome do computador, contas locais exclusivas e determinados identificadores do sistema.

Isso permite que a mesma versão seja utilizada para criar várias VMs independentes.

Uma imagem Specialized, por outro lado, preserva o estado e diversas configurações da máquina de origem. Esse modelo pode ser útil em cenários específicos, mas não será utilizado neste laboratório.

Arquitetura do laboratório

A arquitetura final do laboratório será parecida com esta:

GOVERNANÇA E PERMISSÕES

flowchart TB
    TENANT["Microsoft Entra Tenant"]
    ROOT["Tenant Root Group"]
    MG["Management Group<br/>Plataforma / Landing Zones"]

    POLICY["Azure Policy Assignment<br/>Guardrails corporativos"]
    ROLEDEF["Custom Role Definition<br/>AIB Permissions<br/>Assignable Scope: Management Group"]

    SUBPRD["Subscription<br/>Produção"]
    SUBHML["Subscription<br/>Homologação"]
    SUBIMG["Subscription de Imagens<br/>Image Factory / Shared Services"]

    TENANT --> ROOT
    ROOT --> MG

    MG --> SUBPRD
    MG --> SUBHML
    MG --> SUBIMG

    POLICY -. "Atribuição no Management Group" .-> MG
    ROLEDEF -. "Disponível para atribuição<br/>nos escopos descendentes" .-> MG

REDE E PROCESSO DE BUILD

flowchart LR
    PIP["Public IP Standard"] --> NAT["NAT Gateway<br/>Saída explícita"]

    subgraph VNET["VNet dedicada"]
        direction TB

        SB["Subnet de Build<br/>VM temporária"]
        SA["Subnet de ACI<br/>Delegada"]
    end

    NAT --> SB
    NAT --> SA

    AIB["Azure VM Image Builder"] --> SRG["Staging Resource Group<br/>Recursos temporários"]

    SRG --> VM["VM temporária de Build"]
    SRG --> ACI["Azure Container Instance<br/>Build isolado"]

    SB --> VM
    SA --> ACI

    VM --> CUSTOM["Customizações<br/>Atualizações e configurações"]
    ACI --> CUSTOM

    CUSTOM --> VALIDATE["Validações da imagem"]
    VALIDATE --> OUTPUT["Artefato de imagem<br/>Pronto para publicação"]

PUBLICAÇÃO E VALIDAÇÃO DA IMAGEM CRIADA

flowchart LR
    OUTPUT["Artefato produzido<br/>pelo Image Builder"]

    ACG["Azure Compute Gallery"] --> IDF["Image Definition<br/>Windows ou Linux"]

    OUTPUT --> VERSION["Image Version<br/>1.0.0, 1.0.1..."]
    IDF --> VERSION

    VERSION --> TESTVM["VM de validação<br/>Sem IP público"]
    TESTNET["Subnet de Teste"] --> TESTVM

    TESTVM --> TESTS["Testes de boot, agente,<br/>rede e customizações"]

    TESTS -->|Aprovada| READY["Imagem homologada<br/>Pronta para consumo"]
    TESTS -->|Reprovada| FIX["Ajustar template<br/>e executar novo build"]

    FIX --> OUTPUT

Sobre a nomenclatura

Para manter um padrão mais próximo do Cloud Adoption Framework, vou usar nomes mais organizados e fáceis de entender.

RecursoNome sugerido
Resource Grouprg-aib-lab-wus2-001
Virtual Networkvnet-aib-lab-wus2-001
Subnet para Build VMsnet-aib-build-wus2-001
Subnet para ACIsnet-aib-aci-wus2-001
Subnet para VMs de testesnet-aib-test-wus2-001
Public IP do NAT Gatewaypip-nat-aib-lab-wus2-001
NAT Gatewaynat-aib-lab-wus2-001
Managed Identityid-aib-lab-wus2-001
Azure Compute Gallerygal_aib_lab_wus2_001
Custom Rolerole-aib-lab-image-builder
Image Definition Windows 2022imgdef-winsrv2022
Image Definition Windows 2025imgdef-winsrv2025
Image Definition Ubuntu 24.04imgdef-ubuntu2404
Image Definition Debian 13imgdef-debian13

A Azure Compute Gallery não aceita hífen em todos os campos da mesma forma que outros recursos, por isso o nome da galeria está usando underline. Esse tipo de detalhe parece pequeno, mas evita erro bobo durante o laboratório.

Utilizaremos um NAT Gateway para garantir a saída para a internet. Nas versões de API liberadas depois de 31 de março de 2026, as subnets de VNets novas nascem com defaultOutboundAccess igual a false, ou seja, privadas, e o Portal já cria as subnets desse jeito. Sem um método explícito de saída, a VM de build não alcança o Windows Update, os repositórios APT nem os endpoints públicos do Azure.


Pré-requisitos

Antes de iniciar, vamos considerar alguns pré-requisitos:

  • Permissão de Owner para criar Resource Groups, VNet, Managed Identity, Compute Gallery e Custom RBAC;
  • Região com suporte ao Azure VM Image Builder;
  • Cotas disponíveis para criação temporária de recursos de build;
  • Nenhuma Azure Policy bloqueando recursos temporários, como:
    • Virtual Machine;
    • Network Interface;
    • Disk;
    • Storage Account;
    • Azure Container Instance;
    • Network Security Group;
    • Private Endpoint, dependendo da topologia.

O Azure Image Builder cria recursos temporários durante o build. Então, se existir uma Azure Policy muito restritiva permitindo apenas alguns tipos de recursos, o build pode falhar mesmo que o template esteja correto.

Esse laboratório gera custo. O NAT Gateway cobra por hora e por GB processado mesmo sem nenhum build rodando, e cada versão publicada na galeria cobra armazenamento em cada região replicada. Se for só estudo, rode a limpeza do final do artigo no mesmo dia.


Imagens base que serão utilizadas

Vamos utilizar imagens oficiais do Marketplace.

Windows Server 2022

1
2
3
4
Publisher: MicrosoftWindowsServer
Offer: WindowsServer
SKU: 2022-datacenter-azure-edition
Version: latest

Windows Server 2025

1
2
3
4
Publisher: MicrosoftWindowsServer
Offer: WindowsServer
SKU: 2025-datacenter-azure-edition
Version: latest

Ubuntu 24.04 LTS

1
2
3
4
Publisher: Canonical
Offer: ubuntu-24_04-lts
SKU: server
Version: latest

Debian 13

1
2
3
4
Publisher: Debian
Offer: debian-13
SKU: 13-gen2
Version: latest

Passo 1 - Registrar os providers necessários

Antes de usar o Azure Image Builder, precisamos garantir que alguns providers estejam registrados na assinatura.

No Cloud Shell:

1
2
3
4
5
6
7
az provider register --namespace Microsoft.VirtualMachineImages
az provider register --namespace Microsoft.Compute
az provider register --namespace Microsoft.KeyVault
az provider register --namespace Microsoft.Storage
az provider register --namespace Microsoft.Network
az provider register --namespace Microsoft.ContainerInstance
az provider register --namespace Microsoft.ManagedIdentity

azure-image-builder

Em seguida valide se os providers foram registrados corretamente:

1
2
3
4
az provider show --namespace Microsoft.VirtualMachineImages --query registrationState -o tsv
az provider show --namespace Microsoft.Compute --query registrationState -o tsv
az provider show --namespace Microsoft.Network --query registrationState -o tsv
az provider show --namespace Microsoft.ContainerInstance --query registrationState -o tsv

azure-image-builder

O retorno esperado é Registered

Se algum provider ainda aparecer como Registering, aguarde alguns minutos e valide novamente. Essa etapa é simples, mas é uma das primeiras coisas que podem causar erro se for esquecida.


Passo 2 - Criar o Resource Group

Vamos começar criando o Resource Group do laboratório.

Pelo portal:

  1. Acesse o Azure Portal;
  2. Pesquise por Resource groups;
  3. Clique em Create;
  4. Informe:
    • Subscription: Informe a subscription que será utilizada para a geração das imagens e alocação dos demais recursos;
    • Resource group:
      1
      
      rg-aib-lab-wus2-001
      
    • Region: West US 2;
  5. Clique em Review + Create;
  6. Clique em Create.

azure-image-builder


Passo 3 - Criar a VNet dedicada para o build

Agora vamos criar uma VNet dedicada para o processo de build.

Neste exemplo iremos utilizar 3 subnets:

SubnetFinalidade
snet-aib-build-wus2-001Subnet onde a VM temporária de build será criada
snet-aib-aci-wus2-001Subnet reservada para o Azure Container Instance usado no build isolado
snet-aib-test-wus2-001Subnet onde as VMs de validação da imagem serão criadas no final do laboratório

O Azure Container Instance não é um componente da imagem final e também não é uma aplicação que estamos hospedando. Ele será utilizado pelo Azure VM Image Builder durante o processo de Isolated Image Build, como recurso temporário responsável por parte da execução e orquestração da customização da imagem.

  1. Pesquise por Virtual networks;
  2. Clique em Create;
  3. Informe:
    • Resource group: rg-aib-lab-wus2-001;
    • Name:
      1
      
      vnet-aib-lab-wus2-001
      
    • Region: West US 2;
  4. Em IP Addresses, configure:

    • Address space:
      1
      
      10.250.0.0/16
      
  5. Crie as subnets:

    • Subnet para criação da VM Temporária:
      1
      
      snet-aib-build-wus2-001
      

      com

      1
      
      10.250.1.0/24
      
    • Subnet para criação do ACI de forma isolada:
      1
      
       snet-aib-aci-wus2-001
      

      com

      1
      
       10.250.2.0/24
      
    • Subnet para as VMs de validação da imagem:
      1
      
       snet-aib-test-wus2-001
      

      com

      1
      
       10.250.3.0/24
      
  6. Clique em Review + Create;
  7. Clique em Create.

azure-image-builder

Repare na opção Private subnet durante a criação. O Portal já marca as subnets novas como privadas, sem o antigo acesso de saída implícito. É exatamente por isso que o próximo passo cria um NAT Gateway: sem ele, nada dentro dessa VNet chega à internet.


Passo 4 - Criando o NAT Gateway

Como mencionei anteriormente, as subnets que criamos no passo anterior nasceram privadas, sem o antigo acesso de saída padrão.

Como o processo de build precisará acessar repositórios de atualização, endpoints do Azure e fontes de pacotes, configuraremos uma saída explícita por meio de um NAT Gateway.

  1. Pesquise por NAT Gateways;
  2. Clique em Create;
  3. Informe:
    • Resource group: rg-aib-lab-wus2-001;
    • Name:
      1
      
      nat-aib-lab-wus2-001
      
    • Region: West US 2;
    • SKU: Selecione a opção Standard
  4. Em Outbound IP, configure:
    • Clique em Add public IP addresses or prefixes;
    • Clique em Create a public IP address;
    • Name:
      1
      
      pip-nat-aib-lab-wus2-001
      
    • Clique em OK e em seguida em SAVE

azure-image-builder

  1. Em Networking:
    • Virtual Network: selecione vnet-aib-lab-wus2-001;
    • Select specific subnets: selecione snet-aib-build-wus2-001, snet-aib-aci-wus2-001 e snet-aib-test-wus2-001
  2. Clique em Review + Create;
  3. Clique em Create.

azure-image-builder


Passo 5 - Preparar a subnet do ACI e a Managed Identity

Delegando a subnet do ACI

Para o cenário com build isolado e subnet dedicada para o Azure Container Instance, precisamos delegar a subnet do ACI.

Para esse passo, vamos utilizar o Cloud Shell:

1
2
3
4
5
az network vnet subnet update \
  --resource-group rg-aib-lab-wus2-001 \
  --vnet-name vnet-aib-lab-wus2-001 \
  --name snet-aib-aci-wus2-001 \
  --delegations Microsoft.ContainerInstance/containerGroups

azure-image-builder

Agora vamos guardar os IDs das subnets, pois eles serão usados no template do Image Builder:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
BUILD_SUBNET_ID=$(az network vnet subnet show \
  --resource-group rg-aib-lab-wus2-001 \
  --vnet-name vnet-aib-lab-wus2-001 \
  --name snet-aib-build-wus2-001 \
  --query id \
  -o tsv)

ACI_SUBNET_ID=$(az network vnet subnet show \
  --resource-group rg-aib-lab-wus2-001 \
  --vnet-name vnet-aib-lab-wus2-001 \
  --name snet-aib-aci-wus2-001 \
  --query id \
  -o tsv)

echo $BUILD_SUBNET_ID
echo $ACI_SUBNET_ID

azure-image-builder

Guarde esses valores. Eles serão usados no vmProfile dos templates do Azure Image Builder.

Criando a User Assigned Managed Identity

O Azure Image Builder precisa de uma identidade para conseguir escrever a imagem final na Azure Compute Gallery e também interagir com alguns recursos necessários durante o build.

Criaremos uma User Assigned Managed Identity:

  1. Pesquise por Managed Identities e clique em Create:

azure-image-builder

  1. Informe:

    • Resource group: rg-aib-lab-wus2-001;
    • Region: West US 2;
    • Name:
      1
      
      id-aib-lab-wus2-001
      
  2. Não será necessário selecionar Isolation Scope, basta clicar em Next;
  3. Coloque as tags padrões elegíveis na governança conforme a sua necessidade;
  4. Clique em Create.

azure-image-builder

Agora vamos capturar os IDs da identidade, pois iremos utilizá-los nos próximos passos:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
SUBSCRIPTION_ID=$(az account show --query id -o tsv)

IDENTITY_ID=$(az identity show \
  --resource-group rg-aib-lab-wus2-001 \
  --name id-aib-lab-wus2-001 \
  --query id \
  -o tsv)

IDENTITY_CLIENT_ID=$(az identity show \
  --resource-group rg-aib-lab-wus2-001 \
  --name id-aib-lab-wus2-001 \
  --query clientId \
  -o tsv)

IDENTITY_PRINCIPAL_ID=$(az identity show \
  --resource-group rg-aib-lab-wus2-001 \
  --name id-aib-lab-wus2-001 \
  --query principalId \
  -o tsv)

echo $IDENTITY_ID
echo $IDENTITY_CLIENT_ID
echo $IDENTITY_PRINCIPAL_ID

azure-image-builder


Agora vamos criar o repositório que receberá as imagens customizadas.

Pule esta etapa caso você já tenha uma galeria criada.

Pelo portal:

  1. Pesquise por Azure Compute Galleries;
  2. Clique em Create;
  3. Na aba Basics informe:
    • Resource group: rg-aib-lab-wus2-001;
    • Name:
      1
      
      gal_aib_lab_wus2_001
      
    • Region: West US 2;
    • Description: Esta galeria de imagens será utilizada para criações padronizadas exclusivas ao Azure Image Builder - A descrição é opcional, mas altamente recomendável

azure-image-builder

  1. Na aba Sharing manteremos o método via RBAC, pois não iremos produzir uma imagem a ser compartilhada publicamente para comunidade;
  2. Adicione as Tags conforme a governança da sua empresa;
  3. Clique em Review + Create;
  4. Clique em Create.

azure-image-builder


Passo 7 - Criando as Image Definitions

A Azure Compute Gallery trabalha com alguns conceitos importantes:

ConceitoExplicação
GalleryGaleria onde as imagens ficam organizadas
Image DefinitionDefinição lógica da imagem
Image VersionVersão publicada da imagem
GeneralizedImagem preparada para criar novas VMs
SpecializedImagem ainda ligada à identidade/configuração original da VM

As quatro definições serão criadas como:

  • Generalized;
  • Hyper-V Generation V2;
  • TrustedLaunchSupported.

A definição TrustedLaunchSupported permite que a imagem seja utilizada tanto em VMs Gen2 padrão quanto em VMs configuradas com Trusted Launch.

Darei preferência em trabalhar com imagens Generalized, que é o cenário mais comum para criação de novas VMs padronizadas.

As atividades abaixo utilizaremos o Cloud Shell:

Windows Server 2022

1
2
3
4
5
6
7
8
9
10
11
az sig image-definition create \
  --resource-group rg-aib-lab-wus2-001 \
  --gallery-name gal_aib_lab_wus2_001 \
  --gallery-image-definition imgdef-winsrv2022 \
  --publisher RuizSolutions \
  --offer base-windows \
  --sku winsrv2022 \
  --os-type Windows \
  --os-state Generalized \
  --hyper-v-generation V2 \
  --features SecurityType=TrustedLaunchSupported

azure-image-builder
—

Windows Server 2025

1
2
3
4
5
6
7
8
9
10
11
az sig image-definition create \
  --resource-group rg-aib-lab-wus2-001 \
  --gallery-name gal_aib_lab_wus2_001 \
  --gallery-image-definition imgdef-winsrv2025 \
  --publisher RuizSolutions \
  --offer base-windows \
  --sku winsrv2025 \
  --os-type Windows \
  --os-state Generalized \
  --hyper-v-generation V2 \
  --features SecurityType=TrustedLaunchSupported

azure-image-builder
—

Ubuntu 24.04

1
2
3
4
5
6
7
8
9
10
11
az sig image-definition create \
  --resource-group rg-aib-lab-wus2-001 \
  --gallery-name gal_aib_lab_wus2_001 \
  --gallery-image-definition imgdef-ubuntu2404 \
  --publisher RuizSolutions \
  --offer base-linux \
  --sku ubuntu2404 \
  --os-type Linux \
  --os-state Generalized \
  --hyper-v-generation V2 \
  --features SecurityType=TrustedLaunchSupported

azure-image-builder
—

Debian 13

1
2
3
4
5
6
7
8
9
10
11
az sig image-definition create \
  --resource-group rg-aib-lab-wus2-001 \
  --gallery-name gal_aib_lab_wus2_001 \
  --gallery-image-definition imgdef-debian13 \
  --publisher RuizSolutions \
  --offer base-linux \
  --sku debian13 \
  --os-type Linux \
  --os-state Generalized \
  --hyper-v-generation V2 \
  --features SecurityType=TrustedLaunchSupported

azure-image-builder

O Hyper-V Generation da definição da imagem precisa bater com a geração da imagem base. Se você criar uma definição Gen1 e tentar publicar uma imagem Gen2, o processo vai falhar.


Passo 8 - Criando uma ROLE customizada para o Azure Image Builder

Agora vem uma das partes mais importantes do laboratório: PERMISSÕES

A Managed Identity será utilizada durante o build, mas ela não recebe acesso automático à VNet ou à Azure Compute Gallery.

O Azure Image Builder precisa conseguir:

  • Ler a galeria;
  • Ler as definições de imagem;
  • Criar versões de imagem;
  • Criar e remover a imagem intermediária gerada durante o processo;
  • Ler a VNet;
  • Fazer join na subnet.

Repare no que não está nessa lista: os recursos temporários do build. Quando registramos o provider Microsoft.VirtualMachineImages lá no Passo 1, o serviço do Image Builder passou a ter permissão para criar, gerenciar e excluir o staging resource group na assinatura. A VM temporária, a NIC, o NSG, o Storage Account e o Azure Container Instance são criados pelo próprio serviço, não pela sua Managed Identity. É por isso que a role abaixo é bem menor do que a maioria dos tutoriais espalhados por aí.

Para este laboratório também não precisamos conceder Contributor à Managed Identity no Resource Group inteiro.

Criando a Custom Role pelo Portal

Adicionar permissão por permissão na tela é trabalhoso e fácil de errar. Vamos subir um JSON pronto.

Acesse o meu repositório e baixe o arquivo Custom_Role_AIB.json

Conteúdo do arquivo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
  "properties": {
    "roleName": "role-aib-lab-image-builder",
    "description": "Permissões necessárias para o Azure VM Image Builder publicar imagens na Azure Compute Gallery utilizando VNet dedicada.",
    "assignableScopes": [
      "/providers/Microsoft.Management/managementGroups/<MANAGEMENT_GROUP_ID>"
    ],
    "permissions": [
      {
        "actions": [
          "Microsoft.Compute/galleries/read",
          "Microsoft.Compute/galleries/images/read",
          "Microsoft.Compute/galleries/images/versions/read",
          "Microsoft.Compute/galleries/images/versions/write",
          "Microsoft.Compute/images/read",
          "Microsoft.Compute/images/write",
          "Microsoft.Compute/images/delete",
          "Microsoft.Network/virtualNetworks/read",
          "Microsoft.Network/virtualNetworks/subnets/join/action"
        ],
        "notActions": [],
        "dataActions": [],
        "notDataActions": []
      }
    ]
  }
}

As três ações de Microsoft.Compute/images costumam ser esquecidas quando o destino é só a galeria. A documentação oficial trata elas como o conjunto base de distribuição e as ações de galleries como adicionais. Sem elas o build customiza a imagem inteira e falha na hora de publicar, depois de você já ter esperado quase uma hora.

Antes de subir o arquivo, troque o <MANAGEMENT_GROUP_ID> pelo ID do seu Management Group. Se não souber de cabeça, o Cloud Shell devolve a lista:

1
az account management-group list --query "[].{Nome:displayName, Id:name}" -o table

Esse JSON está no formato que o Portal espera na opção Start from JSON, com o wrapper properties. O formato aceito pelo az role definition create é diferente, com Name, IsCustom, Actions e AssignableScopes na raiz do arquivo. Os dois não são intercambiáveis, então não misture.

  1. Acesse o Management Group que contém a Subscription de Imagens - No meu caso eu tenho apenas 1 subscriptions localizada em LABS - PRD; azure-image-builder

  2. Acesse Access control (IAM);
  3. Clique em Add > Add custom role;
  4. Na aba Basics, em Baseline permissions, selecione Start from JSON e envie o arquivo Custom_Role_AIB.json;
  5. Confira os campos carregados do arquivo:
    • Custom role name:
      1
      
      role-aib-lab-image-builder
      
    • Description: Permissões necessárias para o Azure VM Image Builder publicar imagens na Azure Compute Gallery utilizando VNet dedicada;
  6. Na aba Permissions, confirme que as nove ações vieram do arquivo:
1
2
3
4
5
6
7
8
9
Microsoft.Compute/galleries/read
Microsoft.Compute/galleries/images/read
Microsoft.Compute/galleries/images/versions/read
Microsoft.Compute/galleries/images/versions/write
Microsoft.Compute/images/read
Microsoft.Compute/images/write
Microsoft.Compute/images/delete
Microsoft.Network/virtualNetworks/read
Microsoft.Network/virtualNetworks/subnets/join/action
  1. Na aba Assignable scopes, confirme que o Management Group está listado. É essa propriedade que define onde a role pode ser atribuída depois, e ela não concede acesso nenhum sozinha;
  2. Clique em Review + create e depois em Create.

A role definida no Management Group fica disponível para todas as subscriptions abaixo dele, o que evita recriar a mesma definição em cada assinatura. A atribuição, essa sim, vamos manter no menor escopo possível.

Atribuindo a role à Managed Identity

Criar a Role Definition não dá acesso a ninguém. Enquanto não existir um Role Assignment, a identidade continua sem permissão e o primeiro build vai falhar na publicação.

Vamos atribuir a role apenas no Resource Group do laboratório:

  1. Acesse o Resource Group rg-aib-lab-wus2-001;
  2. Clique em Access control (IAM);
  3. Clique em Add > Add role assignment;
  4. Na aba Role, procure por role-aib-lab-image-builder na guia de roles customizadas e selecione;
  5. Na aba Members:
    • Assign access to: Managed identity;
    • Clique em Select members e escolha User-assigned managed identity;
    • Selecione id-aib-lab-wus2-001;
  6. Clique em Review + assign.

Se preferir resolver pelo Cloud Shell, usando as variáveis que capturamos no Passo 5:

1
2
3
4
5
az role assignment create \
  --assignee-object-id $IDENTITY_PRINCIPAL_ID \
  --assignee-principal-type ServicePrincipal \
  --role "role-aib-lab-image-builder" \
  --scope /subscriptions/$SUBSCRIPTION_ID/resourceGroups/rg-aib-lab-wus2-001

O resultado que queremos é esse:

ItemValor
Principalid-aib-lab-wus2-001
Rolerole-aib-lab-image-builder
Escoporg-aib-lab-wus2-001

Em alguns casos, a propagação de RBAC pode levar alguns minutos. Se o build falhar logo após a criação da role, aguarde um pouco e tente novamente antes de sair alterando tudo.


Passo 9 - Entender o que o Image Builder cria nos bastidores

Antes de criar a primeira imagem, vale entender uma coisa importante.

Quando você executa o Azure VM Image Builder, ele cria recursos temporários na assinatura para realizar o build. Esse conjunto é criado e destruído pelo próprio serviço, com a permissão que veio do registro do provider, e não pela Managed Identity que configuramos no passo anterior.

Você provavelmente verá um Resource Group temporário parecido com:

1
IT_rg-aib-lab-wus2-001_<nome-do-template>

Dentro dele podem aparecer recursos como:

  • VM temporária de build;
  • Discos temporários;
  • NIC;
  • Storage Account;
  • Azure Container Instance;
  • NSG;
  • Recursos de rede temporários.

Esses recursos existem durante o processo de build e são removidos ao final.

Não saia apagando manualmente o Resource Group temporário no meio do processo. Se for necessário limpar, o ideal é excluir primeiro o Image Template. Apagar recursos temporários na mão pode deixar o template em estado inconsistente.


Passo 10 - Criar o template da imagem Windows Server 2022

Agora vamos criar a primeira imagem.

Os quatro templates deste laboratório também estão no meu repositório, se preferir baixar em vez de copiar: Azure Image Builder

Crie o arquivo:

1
aib-winsrv2022-template.json

Conteúdo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
{
  "type": "Microsoft.VirtualMachineImages/imageTemplates",
  "apiVersion": "2024-02-01",
  "location": "westus2",
  "identity": {
    "type": "UserAssigned",
    "userAssignedIdentities": {
      "<IDENTITY_ID>": {}
    }
  },
  "properties": {
    "buildTimeoutInMinutes": 180,
    "source": {
      "type": "PlatformImage",
      "publisher": "MicrosoftWindowsServer",
      "offer": "WindowsServer",
      "sku": "2022-datacenter-azure-edition",
      "version": "latest"
    },
    "customize": [
      {
        "type": "PowerShell",
        "name": "CreateBaseFolders",
        "inline": [
          "New-Item -Path 'C:\\Temp' -ItemType Directory -Force",
          "New-Item -Path 'C:\\BuildInfo' -ItemType Directory -Force",
          "Set-Content -Path 'C:\\BuildInfo\\image-info.txt' -Value 'Imagem base Windows Server 2022 criada via Azure VM Image Builder'"
        ]
      },
      {
        "type": "PowerShell",
        "name": "InstallBasicWindowsFeatures",
        "inline": [
          "Install-WindowsFeature -Name Web-Server -IncludeManagementTools",
          "Set-TimeZone -Id 'E. South America Standard Time'"
        ]
      },
      {
        "type": "WindowsUpdate",
        "name": "InstallWindowsUpdates",
        "searchCriteria": "IsInstalled=0",
        "filters": [
          "exclude:$_.Title -like '*Preview*'",
          "include:$true"
        ],
        "updateLimit": 20
      },
      {
        "type": "WindowsRestart",
        "name": "RestartAfterUpdates",
        "restartTimeout": "30m"
      },
      {
        "type": "PowerShell",
        "name": "CorporateOptionalExamples",
        "inline": [
          "New-Item -Path 'C:\\Corporate' -ItemType Directory -Force",
          "Set-Content -Path 'C:\\Corporate\\README.txt' -Value 'Aqui poderiam entrar agentes corporativos, hardening, scripts de segurança ou ferramentas internas.'"
        ]
      }
    ],
    "distribute": [
      {
        "type": "SharedImage",
        "galleryImageId": "/subscriptions/<SUBSCRIPTION_ID>/resourceGroups/rg-aib-lab-wus2-001/providers/Microsoft.Compute/galleries/gal_aib_lab_wus2_001/images/imgdef-winsrv2022",
        "runOutputName": "runoutput-winsrv2022",
        "artifactTags": {
          "source": "azure-image-builder",
          "os": "windows-server-2022",
          "environment": "lab"
        },
        "replicationRegions": [
          "westus2"
        ],
        "excludeFromLatest": false
      }
    ],
    "vmProfile": {
      "vmSize": "Standard_D2ds_v5",
      "osDiskSizeGB": 127,
      "vnetConfig": {
        "subnetId": "<BUILD_SUBNET_ID>",
        "containerInstanceSubnetId": "<ACI_SUBNET_ID>"
      }
    }
  }
}

Agora substitua os parâmetros:

1
2
3
4
sed -i "s|<IDENTITY_ID>|$IDENTITY_ID|g" aib-winsrv2022-template.json
sed -i "s|<SUBSCRIPTION_ID>|$SUBSCRIPTION_ID|g" aib-winsrv2022-template.json
sed -i "s|<BUILD_SUBNET_ID>|$BUILD_SUBNET_ID|g" aib-winsrv2022-template.json
sed -i "s|<ACI_SUBNET_ID>|$ACI_SUBNET_ID|g" aib-winsrv2022-template.json

Crie o Image Template:

1
2
3
4
5
6
az resource create \
  --resource-group rg-aib-lab-wus2-001 \
  --name aib-winsrv2022 \
  --resource-type Microsoft.VirtualMachineImages/imageTemplates \
  --properties @aib-winsrv2022-template.json \
  --is-full-object

Agora execute o build:

1
2
3
4
5
az resource invoke-action \
  --resource-group rg-aib-lab-wus2-001 \
  --resource-type Microsoft.VirtualMachineImages/imageTemplates \
  --name aib-winsrv2022 \
  --action Run

Passo 11 - Acompanhar o build da imagem Windows Server 2022

Durante o build, acesse:

  1. Resource Group rg-aib-lab-wus2-001;
  2. Procure pelo recurso aib-winsrv2022;
  3. Acesse o template;
  4. Valide o status da última execução.

Também é possível acompanhar pelo Cloud Shell:

1
2
3
4
5
az resource show \
  --resource-group rg-aib-lab-wus2-001 \
  --resource-type Microsoft.VirtualMachineImages/imageTemplates \
  --name aib-winsrv2022 \
  --query properties.lastRunStatus

O retorno esperado ao final é algo como:

1
2
3
4
5
{
  "runState": "Succeeded",
  "runSubState": "Completed",
  "message": ""
}

Após o build ser concluído, acesse:

  1. Azure Compute Gallery;
  2. Galeria gal_aib_lab_wus2_001;
  3. Image definition imgdef-winsrv2022;
  4. Clique em Versions.

Você deverá visualizar uma versão criada automaticamente.


Passo 13 - Criar o template da imagem Windows Server 2025

Agora vamos repetir o processo para Windows Server 2025.

A ideia é a mesma, alterando:

  • Nome do template;
  • SKU da imagem base;
  • Image Definition de destino;
  • Run Output Name;
  • Tags.

Crie o arquivo:

1
aib-winsrv2025-template.json

Conteúdo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
{
  "type": "Microsoft.VirtualMachineImages/imageTemplates",
  "apiVersion": "2024-02-01",
  "location": "westus2",
  "identity": {
    "type": "UserAssigned",
    "userAssignedIdentities": {
      "<IDENTITY_ID>": {}
    }
  },
  "properties": {
    "buildTimeoutInMinutes": 180,
    "source": {
      "type": "PlatformImage",
      "publisher": "MicrosoftWindowsServer",
      "offer": "WindowsServer",
      "sku": "2025-datacenter-azure-edition",
      "version": "latest"
    },
    "customize": [
      {
        "type": "PowerShell",
        "name": "CreateBaseFolders",
        "inline": [
          "New-Item -Path 'C:\\Temp' -ItemType Directory -Force",
          "New-Item -Path 'C:\\BuildInfo' -ItemType Directory -Force",
          "Set-Content -Path 'C:\\BuildInfo\\image-info.txt' -Value 'Imagem base Windows Server 2025 criada via Azure VM Image Builder'"
        ]
      },
      {
        "type": "PowerShell",
        "name": "InstallBasicWindowsFeatures",
        "inline": [
          "Install-WindowsFeature -Name Web-Server -IncludeManagementTools",
          "Set-TimeZone -Id 'E. South America Standard Time'"
        ]
      },
      {
        "type": "WindowsUpdate",
        "name": "InstallWindowsUpdates",
        "searchCriteria": "IsInstalled=0",
        "filters": [
          "exclude:$_.Title -like '*Preview*'",
          "include:$true"
        ],
        "updateLimit": 20
      },
      {
        "type": "WindowsRestart",
        "name": "RestartAfterUpdates",
        "restartTimeout": "30m"
      },
      {
        "type": "PowerShell",
        "name": "CorporateOptionalExamples",
        "inline": [
          "New-Item -Path 'C:\\Corporate' -ItemType Directory -Force",
          "Set-Content -Path 'C:\\Corporate\\README.txt' -Value 'Exemplo de ponto para instalação de Azure Monitor Agent, Defender, ferramentas internas ou hardening corporativo.'"
        ]
      }
    ],
    "distribute": [
      {
        "type": "SharedImage",
        "galleryImageId": "/subscriptions/<SUBSCRIPTION_ID>/resourceGroups/rg-aib-lab-wus2-001/providers/Microsoft.Compute/galleries/gal_aib_lab_wus2_001/images/imgdef-winsrv2025",
        "runOutputName": "runoutput-winsrv2025",
        "artifactTags": {
          "source": "azure-image-builder",
          "os": "windows-server-2025",
          "environment": "lab"
        },
        "replicationRegions": [
          "westus2"
        ],
        "excludeFromLatest": false
      }
    ],
    "vmProfile": {
      "vmSize": "Standard_D2ds_v5",
      "osDiskSizeGB": 127,
      "vnetConfig": {
        "subnetId": "<BUILD_SUBNET_ID>",
        "containerInstanceSubnetId": "<ACI_SUBNET_ID>"
      }
    }
  }
}

Substitua os parâmetros:

1
2
3
4
sed -i "s|<IDENTITY_ID>|$IDENTITY_ID|g" aib-winsrv2025-template.json
sed -i "s|<SUBSCRIPTION_ID>|$SUBSCRIPTION_ID|g" aib-winsrv2025-template.json
sed -i "s|<BUILD_SUBNET_ID>|$BUILD_SUBNET_ID|g" aib-winsrv2025-template.json
sed -i "s|<ACI_SUBNET_ID>|$ACI_SUBNET_ID|g" aib-winsrv2025-template.json

Crie o template:

1
2
3
4
5
6
az resource create \
  --resource-group rg-aib-lab-wus2-001 \
  --name aib-winsrv2025 \
  --resource-type Microsoft.VirtualMachineImages/imageTemplates \
  --properties @aib-winsrv2025-template.json \
  --is-full-object

Execute o build:

1
2
3
4
5
az resource invoke-action \
  --resource-group rg-aib-lab-wus2-001 \
  --resource-type Microsoft.VirtualMachineImages/imageTemplates \
  --name aib-winsrv2025 \
  --action Run

Passo 14 - Criar o template da imagem Ubuntu 24.04

Agora vamos para Linux.

A estrutura muda um pouco porque agora os customizers são do tipo Shell.

Crie o arquivo:

1
aib-ubuntu2404-template.json

Conteúdo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
{
  "type": "Microsoft.VirtualMachineImages/imageTemplates",
  "apiVersion": "2024-02-01",
  "location": "westus2",
  "identity": {
    "type": "UserAssigned",
    "userAssignedIdentities": {
      "<IDENTITY_ID>": {}
    }
  },
  "properties": {
    "buildTimeoutInMinutes": 120,
    "source": {
      "type": "PlatformImage",
      "publisher": "Canonical",
      "offer": "ubuntu-24_04-lts",
      "sku": "server",
      "version": "latest"
    },
    "customize": [
      {
        "type": "Shell",
        "name": "UpdatePackages",
        "inline": [
          "sudo apt-get update -y",
          "sudo DEBIAN_FRONTEND=noninteractive apt-get upgrade -y"
        ]
      },
      {
        "type": "Shell",
        "name": "InstallBasicPackages",
        "inline": [
          "sudo DEBIAN_FRONTEND=noninteractive apt-get install -y curl wget vim net-tools htop unzip ca-certificates gnupg lsb-release"
        ]
      },
      {
        "type": "Shell",
        "name": "ConfigureTimezone",
        "inline": [
          "sudo timedatectl set-timezone America/Sao_Paulo"
        ]
      },
      {
        "type": "Shell",
        "name": "CreateBuildInfo",
        "inline": [
          "sudo mkdir -p /opt/buildinfo",
          "echo 'Imagem base Ubuntu 24.04 criada via Azure VM Image Builder' | sudo tee /opt/buildinfo/image-info.txt",
          "date | sudo tee -a /opt/buildinfo/image-info.txt"
        ]
      },
      {
        "type": "Shell",
        "name": "ValidateAzureLinuxAgent",
        "inline": [
          "systemctl status walinuxagent --no-pager || true"
        ]
      },
      {
        "type": "Shell",
        "name": "CleanUp",
        "inline": [
          "sudo apt-get autoremove -y",
          "sudo apt-get clean",
          "sudo rm -rf /var/lib/apt/lists/*"
        ]
      }
    ],
    "distribute": [
      {
        "type": "SharedImage",
        "galleryImageId": "/subscriptions/<SUBSCRIPTION_ID>/resourceGroups/rg-aib-lab-wus2-001/providers/Microsoft.Compute/galleries/gal_aib_lab_wus2_001/images/imgdef-ubuntu2404",
        "runOutputName": "runoutput-ubuntu2404",
        "artifactTags": {
          "source": "azure-image-builder",
          "os": "ubuntu-24.04",
          "environment": "lab"
        },
        "replicationRegions": [
          "westus2"
        ],
        "excludeFromLatest": false
      }
    ],
    "vmProfile": {
      "vmSize": "Standard_D2ds_v5",
      "osDiskSizeGB": 64,
      "vnetConfig": {
        "subnetId": "<BUILD_SUBNET_ID>",
        "containerInstanceSubnetId": "<ACI_SUBNET_ID>"
      }
    }
  }
}

Substitua os parâmetros:

1
2
3
4
sed -i "s|<IDENTITY_ID>|$IDENTITY_ID|g" aib-ubuntu2404-template.json
sed -i "s|<SUBSCRIPTION_ID>|$SUBSCRIPTION_ID|g" aib-ubuntu2404-template.json
sed -i "s|<BUILD_SUBNET_ID>|$BUILD_SUBNET_ID|g" aib-ubuntu2404-template.json
sed -i "s|<ACI_SUBNET_ID>|$ACI_SUBNET_ID|g" aib-ubuntu2404-template.json

Crie o template:

1
2
3
4
5
6
az resource create \
  --resource-group rg-aib-lab-wus2-001 \
  --name aib-ubuntu2404 \
  --resource-type Microsoft.VirtualMachineImages/imageTemplates \
  --properties @aib-ubuntu2404-template.json \
  --is-full-object

Execute o build:

1
2
3
4
5
az resource invoke-action \
  --resource-group rg-aib-lab-wus2-001 \
  --resource-type Microsoft.VirtualMachineImages/imageTemplates \
  --name aib-ubuntu2404 \
  --action Run

Passo 15 - Criar o template da imagem Debian 13

Agora vamos criar a imagem Debian 13.

Crie o arquivo:

1
aib-debian13-template.json

Conteúdo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
{
  "type": "Microsoft.VirtualMachineImages/imageTemplates",
  "apiVersion": "2024-02-01",
  "location": "westus2",
  "identity": {
    "type": "UserAssigned",
    "userAssignedIdentities": {
      "<IDENTITY_ID>": {}
    }
  },
  "properties": {
    "buildTimeoutInMinutes": 120,
    "source": {
      "type": "PlatformImage",
      "publisher": "Debian",
      "offer": "debian-13",
      "sku": "13-gen2",
      "version": "latest"
    },
    "customize": [
      {
        "type": "Shell",
        "name": "UpdatePackages",
        "inline": [
          "sudo apt-get update -y",
          "sudo DEBIAN_FRONTEND=noninteractive apt-get upgrade -y"
        ]
      },
      {
        "type": "Shell",
        "name": "InstallBasicPackages",
        "inline": [
          "sudo DEBIAN_FRONTEND=noninteractive apt-get install -y curl wget vim net-tools htop unzip ca-certificates gnupg lsb-release"
        ]
      },
      {
        "type": "Shell",
        "name": "ConfigureTimezone",
        "inline": [
          "sudo timedatectl set-timezone America/Sao_Paulo"
        ]
      },
      {
        "type": "Shell",
        "name": "CreateBuildInfo",
        "inline": [
          "sudo mkdir -p /opt/buildinfo",
          "echo 'Imagem base Debian 13 criada via Azure VM Image Builder' | sudo tee /opt/buildinfo/image-info.txt",
          "date | sudo tee -a /opt/buildinfo/image-info.txt"
        ]
      },
      {
        "type": "Shell",
        "name": "ValidateAzureLinuxAgent",
        "inline": [
          "systemctl status walinuxagent --no-pager || true"
        ]
      },
      {
        "type": "Shell",
        "name": "CleanUp",
        "inline": [
          "sudo apt-get autoremove -y",
          "sudo apt-get clean",
          "sudo rm -rf /var/lib/apt/lists/*"
        ]
      }
    ],
    "distribute": [
      {
        "type": "SharedImage",
        "galleryImageId": "/subscriptions/<SUBSCRIPTION_ID>/resourceGroups/rg-aib-lab-wus2-001/providers/Microsoft.Compute/galleries/gal_aib_lab_wus2_001/images/imgdef-debian13",
        "runOutputName": "runoutput-debian13",
        "artifactTags": {
          "source": "azure-image-builder",
          "os": "debian-13",
          "environment": "lab"
        },
        "replicationRegions": [
          "westus2"
        ],
        "excludeFromLatest": false
      }
    ],
    "vmProfile": {
      "vmSize": "Standard_D2ds_v5",
      "osDiskSizeGB": 64,
      "vnetConfig": {
        "subnetId": "<BUILD_SUBNET_ID>",
        "containerInstanceSubnetId": "<ACI_SUBNET_ID>"
      }
    }
  }
}

Substitua os parâmetros:

1
2
3
4
sed -i "s|<IDENTITY_ID>|$IDENTITY_ID|g" aib-debian13-template.json
sed -i "s|<SUBSCRIPTION_ID>|$SUBSCRIPTION_ID|g" aib-debian13-template.json
sed -i "s|<BUILD_SUBNET_ID>|$BUILD_SUBNET_ID|g" aib-debian13-template.json
sed -i "s|<ACI_SUBNET_ID>|$ACI_SUBNET_ID|g" aib-debian13-template.json

Crie o template:

1
2
3
4
5
6
az resource create \
  --resource-group rg-aib-lab-wus2-001 \
  --name aib-debian13 \
  --resource-type Microsoft.VirtualMachineImages/imageTemplates \
  --properties @aib-debian13-template.json \
  --is-full-object

Execute o build:

1
2
3
4
5
az resource invoke-action \
  --resource-group rg-aib-lab-wus2-001 \
  --resource-type Microsoft.VirtualMachineImages/imageTemplates \
  --name aib-debian13 \
  --action Run

Depois que todos os builds forem concluídos, acesse:

  1. Azure Compute Galleries;
  2. gal_aib_lab_wus2_001;
  3. Valide cada Image Definition:

    • imgdef-winsrv2022;
    • imgdef-winsrv2025;
    • imgdef-ubuntu2404;
    • imgdef-debian13;
  4. Em cada uma, acesse Versions.

Você deve ter uma versão publicada para cada imagem.


Passo 17 - Criar uma VM a partir da imagem customizada

Agora vamos validar se a imagem realmente funciona.

As quatro VMs de teste vão para a snet-aib-test-wus2-001, que já está atrás do NAT Gateway. Por isso elas conseguem atualizar pacotes e falar com os endpoints do Azure mesmo sendo uma subnet privada.

O az vm create cria um IP público para cada VM e abre a porta de RDP ou SSH. Para laboratório resolve, para ambiente real não. Ali você usaria Azure Bastion ou pelo menos restringiria a origem no NSG.

Criando uma VM Windows Server 2022

1
2
3
4
5
6
7
8
9
10
11
az vm create \
  --resource-group rg-aib-lab-wus2-001 \
  --name vm-test-winsrv2022-aib-001 \
  --location westus2 \
  --image "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/rg-aib-lab-wus2-001/providers/Microsoft.Compute/galleries/gal_aib_lab_wus2_001/images/imgdef-winsrv2022/versions/latest" \
  --admin-username azureuser \
  --admin-password "Troque@EssaSenha123456!" \
  --vnet-name vnet-aib-lab-wus2-001 \
  --subnet snet-aib-test-wus2-001 \
  --size Standard_D2s_v5 \
  --security-type TrustedLaunch

Use uma senha segura no seu ambiente. A senha acima está apenas como exemplo para laboratório.

Depois de criada, acesse a VM via RDP ou Bastion e valide:

1
2
3
4
5
dir C:\
dir C:\BuildInfo
dir C:\Corporate
Get-WindowsFeature Web-Server
Get-TimeZone

Criando uma VM Windows Server 2025

1
2
3
4
5
6
7
8
9
10
11
az vm create \
  --resource-group rg-aib-lab-wus2-001 \
  --name vm-test-winsrv2025-aib-001 \
  --location westus2 \
  --image "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/rg-aib-lab-wus2-001/providers/Microsoft.Compute/galleries/gal_aib_lab_wus2_001/images/imgdef-winsrv2025/versions/latest" \
  --admin-username azureuser \
  --admin-password "Troque@EssaSenha123456!" \
  --vnet-name vnet-aib-lab-wus2-001 \
  --subnet snet-aib-test-wus2-001 \
  --size Standard_D2s_v5 \
  --security-type TrustedLaunch

Valide da mesma forma que fizemos no Windows Server 2022:

1
2
3
4
5
dir C:\
dir C:\BuildInfo
dir C:\Corporate
Get-WindowsFeature Web-Server
Get-TimeZone

Criando uma VM Ubuntu 24.04

1
2
3
4
5
6
7
8
9
10
11
az vm create \
  --resource-group rg-aib-lab-wus2-001 \
  --name vm-test-ubuntu2404-aib-001 \
  --location westus2 \
  --image "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/rg-aib-lab-wus2-001/providers/Microsoft.Compute/galleries/gal_aib_lab_wus2_001/images/imgdef-ubuntu2404/versions/latest" \
  --admin-username azureuser \
  --generate-ssh-keys \
  --vnet-name vnet-aib-lab-wus2-001 \
  --subnet snet-aib-test-wus2-001 \
  --size Standard_D2s_v5 \
  --security-type TrustedLaunch

Valide:

1
2
3
4
5
6
cat /opt/buildinfo/image-info.txt
timedatectl
which curl
which wget
which vim
systemctl status walinuxagent --no-pager

Criando uma VM Debian 13

1
2
3
4
5
6
7
8
9
10
11
az vm create \
  --resource-group rg-aib-lab-wus2-001 \
  --name vm-test-debian13-aib-001 \
  --location westus2 \
  --image "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/rg-aib-lab-wus2-001/providers/Microsoft.Compute/galleries/gal_aib_lab_wus2_001/images/imgdef-debian13/versions/latest" \
  --admin-username azureuser \
  --generate-ssh-keys \
  --vnet-name vnet-aib-lab-wus2-001 \
  --subnet snet-aib-test-wus2-001 \
  --size Standard_D2s_v5 \
  --security-type TrustedLaunch

Valide:

1
2
3
4
5
6
cat /opt/buildinfo/image-info.txt
timedatectl
which curl
which wget
which vim
systemctl status walinuxagent --no-pager

O que poderíamos adicionar em um cenário corporativo?

Até aqui fizemos customizações básicas, ótimas para laboratório.

Mas em um ambiente corporativo, o Azure Image Builder pode ir bem além.

Alguns exemplos:

Windows

  • Instalação do Azure Monitor Agent;
  • Instalação de agente de segurança;
  • Configuração de baseline;
  • Instalação de ferramentas internas;
  • Configuração de proxy;
  • Configuração de timezone;
  • Instalação de roles específicas;
  • Aplicação de scripts PowerShell;
  • Cópia de arquivos base;
  • Preparação para workloads específicos.

Linux

  • Instalação de pacotes padrão;
  • Hardening básico;
  • Configuração de NTP/timezone;
  • Configuração de repositórios internos;
  • Instalação de agentes;
  • Ajuste de logs;
  • Configuração de proxy;
  • Instalação de ferramentas de troubleshooting;
  • Preparação para NGINX, Apache, Docker ou aplicações internas.

O ponto importante é: quanto mais coisa você coloca na imagem, mais controle você precisa ter sobre versionamento, documentação e testes. Golden Image sem processo vira só mais uma fonte de problema.


Boas práticas para usar Azure Image Builder

Alguns cuidados que fazem bastante diferença:

1. Não misture tudo em uma única imagem

Evite criar uma imagem “faz tudo”.

O ideal é separar por finalidade:

  • Base Windows Server;
  • Base Linux;
  • Base Web Server;
  • Base Aplicação;
  • Base Banco de Dados, se fizer sentido;
  • Base AVD, se o cenário for esse.

2. Versione as imagens

Use versões como:

1
2
3
4
1.0.0
1.0.1
1.1.0
2.0.0

Exemplo prático:

VersãoSignificado
1.0.0Primeira imagem base
1.0.1Correção pequena
1.1.0Novos pacotes ou ajustes
2.0.0Mudança maior na imagem

3. Teste antes de liberar

Nunca publique uma imagem como padrão sem criar uma VM de teste antes.

Valide:

  • Boot;
  • Login;
  • Rede;
  • Agentes;
  • Atualizações;
  • Logs;
  • Aplicações;
  • Políticas;
  • Monitoramento.

4. Cuidado com secrets

Não grave senha, token, chave de API ou segredo dentro da imagem.

Se precisar acessar secrets durante o build, prefira Key Vault e Managed Identity.

5. Documente cada imagem

Mantenha um histórico simples:

1
2
3
4
5
6
7
8
9
10
Imagem: imgdef-ubuntu2404
Versão: 1.0.0
Data: 07/06/2026
Base: Canonical:ubuntu-24_04-lts:server:latest
Customizações:
- apt update/upgrade
- pacotes básicos
- timezone America/Sao_Paulo
- limpeza de cache
Responsável: Cloud Team

6. Cuidado com Azure Policy

Se houver políticas bloqueando recursos, valide antes:

  • ACI permitido;
  • Storage Account permitido;
  • VM temporária permitida;
  • NSG permitido;
  • Private Endpoint permitido, dependendo da topologia;
  • Região permitida;
  • SKU de VM permitida.

7. Não apague o Resource Group temporário manualmente

Se precisar limpar, remova o Image Template corretamente.


Erros comuns

Erro de permissão na galeria

Normalmente ocorre quando a Managed Identity não tem permissão para gravar versões na Azure Compute Gallery.

Valide:

1
2
3
4
az role assignment list \
  --assignee $IDENTITY_CLIENT_ID \
  --all \
  --output table

Erro de subnet

Pode ocorrer quando a identidade não tem permissão de join na subnet.

A ação necessária é:

1
Microsoft.Network/virtualNetworks/subnets/join/action

Erro de imagem não encontrada

Pode acontecer se a SKU não existir na região.

Valide:

1
2
3
4
5
6
az vm image list \
  --all \
  --location westus2 \
  --publisher Debian \
  --offer debian-13 \
  --output table

Erro por Azure Policy

Se a organização bloqueia determinados recursos, o Image Builder pode falhar ao criar recursos temporários.

Nesses casos, valide:

  • Activity Log;
  • Policy compliance;
  • Resource Group temporário;
  • Mensagem do último run;
  • Logs do build.

Build que trava baixando pacotes ou atualizações

É o erro mais comum depois da mudança de saída padrão. A subnet está privada e sem método explícito de saída, então o apt-get update e o Windows Update ficam esperando até estourar o buildTimeoutInMinutes.

Confirme se a subnet de build está associada ao NAT Gateway:

1
2
3
4
5
az network vnet subnet show \
  --resource-group rg-aib-lab-wus2-001 \
  --vnet-name vnet-aib-lab-wus2-001 \
  --name snet-aib-build-wus2-001 \
  --query "{PrivateSubnet:defaultOutboundAccess, NatGateway:natGateway.id}"

Erro de delegation na subnet do ACI

Se o build isolado não sobe o container, geralmente a subnet do ACI está sem a delegation correta ou já tem outro recurso ocupando ela.

1
2
3
4
5
az network vnet subnet show \
  --resource-group rg-aib-lab-wus2-001 \
  --vnet-name vnet-aib-lab-wus2-001 \
  --name snet-aib-aci-wus2-001 \
  --query delegations

O retorno precisa apontar para Microsoft.ContainerInstance/containerGroups.


Checklist final

Antes de considerar o laboratório finalizado, valide:

  • Resource Group criado;
  • Providers registrados;
  • VNet criada;
  • Subnet de build criada;
  • Subnet de ACI criada e delegada;
  • Subnet de teste criada;
  • Managed Identity criada;
  • Azure Compute Gallery criada;
  • Image Definitions criadas;
  • Role customizada criada no Management Group;
  • Role atribuída à Managed Identity no Resource Group;
  • Template Windows Server 2022 criado;
  • Build Windows Server 2022 concluído;
  • Template Windows Server 2025 criado;
  • Build Windows Server 2025 concluído;
  • Template Ubuntu 24.04 criado;
  • Build Ubuntu 24.04 concluído;
  • Template Debian 13 criado;
  • Build Debian 13 concluído;
  • Versões publicadas na Azure Compute Gallery;
  • VMs de teste criadas para os quatro sistemas operacionais;
  • Customizações validadas;
  • Evidências coletadas;
  • Recursos temporários revisados.

Limpeza do ambiente

Se quiser remover o laboratório depois, comece pelos Image Templates:

1
2
3
4
az resource delete \
  --resource-group rg-aib-lab-wus2-001 \
  --resource-type Microsoft.VirtualMachineImages/imageTemplates \
  --name aib-winsrv2022
1
2
3
4
az resource delete \
  --resource-group rg-aib-lab-wus2-001 \
  --resource-type Microsoft.VirtualMachineImages/imageTemplates \
  --name aib-winsrv2025
1
2
3
4
az resource delete \
  --resource-group rg-aib-lab-wus2-001 \
  --resource-type Microsoft.VirtualMachineImages/imageTemplates \
  --name aib-ubuntu2404
1
2
3
4
az resource delete \
  --resource-group rg-aib-lab-wus2-001 \
  --resource-type Microsoft.VirtualMachineImages/imageTemplates \
  --name aib-debian13

Depois, remova o Resource Group, se for realmente um laboratório:

1
2
3
4
az group delete \
  --name rg-aib-lab-wus2-001 \
  --yes \
  --no-wait

Em ambiente real, muito cuidado com esse comando. Ele remove tudo dentro do Resource Group.


Artigos

NomeLink
Arquivos deste laboratóriohttps://github.com/lfrleite/Ruiz-Online/tree/main/Azure%20Image%20Builder
Azure VM Image Builder overviewhttps://learn.microsoft.com/en-us/azure/virtual-machines/image-builder-overview
Create a Windows VM image by using Azure VM Image Builderhttps://learn.microsoft.com/en-us/azure/virtual-machines/windows/image-builder
Create a Linux image and distribute it to Azure Compute Galleryhttps://learn.microsoft.com/en-us/azure/virtual-machines/linux/image-builder
Configure Azure VM Image Builder permissionshttps://learn.microsoft.com/en-us/azure/virtual-machines/linux/image-builder-permissions-cli
Azure VM Image Builder networking optionshttps://learn.microsoft.com/en-us/azure/virtual-machines/linux/image-builder-networking
Isolated Image Builds for Azure VM Image Builderhttps://learn.microsoft.com/en-us/azure/virtual-machines/security-isolated-image-builds-image-builder
Azure Compute Gallery overviewhttps://learn.microsoft.com/en-us/azure/virtual-machines/shared-image-galleries
Default outbound access in Azurehttps://learn.microsoft.com/en-us/azure/virtual-network/ip-services/default-outbound-access
Create or update Azure custom roles using the Azure portalhttps://learn.microsoft.com/en-us/azure/role-based-access-control/custom-roles-portal
Cloud Adoption Framework - Naming conventionhttps://learn.microsoft.com/pt-br/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming
Ubuntu images on Azurehttps://documentation.ubuntu.com/azure/azure-how-to/instances/find-ubuntu-images/
Debian on Microsoft Azurehttps://wiki.debian.org/Cloud/MicrosoftAzure

The End!

Chegamos ao final de mais um laboratório bem interessante.

E esse é um daqueles temas que parecem simples quando olhamos de longe, mas que na prática envolvem vários detalhes importantes: imagem base, identidade gerenciada, permissões, rede, galeria, versionamento, customização, logs e validação.

O Azure Image Builder resolve um problema muito comum em ambientes corporativos: criar imagens padronizadas sem depender de um processo totalmente manual.

Mas ele também exige cuidado.

Não basta apenas criar uma imagem. É preciso entender como ela foi construída, quais scripts foram executados, qual versão foi publicada, quem tem permissão para usar, como ela será atualizada e como será testada antes de virar padrão.

Para laboratório, o processo que fizemos aqui já entrega bastante coisa.

Para produção, o próximo passo natural seria evoluir esse modelo para um pipeline, usando GitHub Actions, Azure DevOps, Bicep ou Terraform, mantendo os templates versionados em repositório e criando um processo mais controlado de release de imagens.

No fim das contas, Golden Image boa não é aquela cheia de coisa instalada.

Golden Image boa é aquela que nasce documentada, testada, versionada e confiável.

E como sempre: ambiente corporativo não combina com clique perdido e configuração na memória. Documente tudo.

Espero que vocês tenham curtido tanto quanto eu curti de produzi-lo. Deixem seus comentários no meu Linkedin sobre o que você achou e se fez sentido!

Obrigado mais uma vez por me acompanharem até aqui! Nos vemos na próxima!

Esta postagem está licenciada sob CC BY 4.0 pelo autor.